Let's say I've a nested object that contains a distinct pair of String-Integer objects (let's call each such pair as endpoint
). There's a List called pair
which contains exactly 2 endpoint
objects. And another List called pairs
that can contain any number of (say n
) pair
entries and I need to collect every unique pair of Strings along with their corresponding Integer values in a new object.
Consider the following classes for existing object:
public class Endpoint {
private String key;
private Integer number;
public Endpoint(String key, Integer number) {
this.key = key;
this.number = number;
}
// Getters, Setters and toString()
}
public class Pair {
// It will have exactly 2 entries
List<Endpoint> pair;
public Pair(List<Endpoint> pair) {
this.pair = pair;
}
// Getters, Setters and toString()
}
Consider the following object (before transformation), where pairs
is a List<Pair>
. The entries in pairs
can be in any order:
pairs: [
pair: [{"p1",1000},{"p2",2000}],
pair: [{"p1",3000},{"p3",4000}],
pair: [{"p2",5000},{"p3",6000}],
pair: [{"p1",2000},{"p2",3000}],
pair: [{"p1",2001},{"p2",3001}],
pair: [{"p1",4000},{"p3",5000}],
pair: [{"p1",4001},{"p3",5001}],
pair: [{"p2",6000},{"p3",7000}],
pair: [{"p2",6001},{"p3",7001}]
]
Consider the following classes to populate the result:
public class CustomEndpoint {
private String key;
// `numbers` can have any number of entries
private List<Integer> numbers;
public CustomEndpoint(String key, List<Integer> numbers) {
this.key = key;
this.numbers = numbers;
}
// Getters, Setters and toString()
}
public class CustomPair {
// It will have exactly 2 entries
List<CustomEndpoint> pair;
public CustomPair(List<CustomEndpoint> pair) {
this.pair = pair;
}
// Getters, Setters and toString()
}
I need to collect it as follows:
custom-pairs: [
custom-pair: {[{"p1", [1000,2000,2001]}, {"p2", [2000,3000,3001]}]},
custom-pair: {[{"p1", [3000,4000,4001]}, {"p3", [4000,5000,5001]}]},
custom-pair: {[{"p2", [5000,6000,6001]}, {"p3", [6000,7000,7001]}]}
]
where custom-pairs
is a List<CustomPair>
. The order of entries in the numbers
List must be maintained as it were for the input pair
. e.g Since 1000
in p1 was paired with 2000
in p2, if 1000
is the first entry in numbers
List for p1 then 2000
must also be the first entry in numbers
List for p2, for the combination where p1 and p2 are paired together.
How can I do this using Streams in Java?
CodePudding user response:
After the clarifications you've given about the classes and their structure I've updated my answer.
Basically, you could group the Pair
elements in your list pairs
by the keys of the two contained endpoints. To make the code more readable, I've added a method in your Pair
class (getPairKeys
) which returns the two endpoint's keys chained together, but you can avoid it by simply chaining the keys right in the stream if you prefer.
After grouping the Pair
elements by their keys, you'll get a Map
where each chained key maps to the n Pair
instances with the same keys. At this point, you could stream the Map
s entries, map each entry to a CustomPair
, sort them by the first number of the first CustomEndpoint
and ultimately collect the instances.
To make my solution work, I've assumed that the implementation of equals
(and so also of hashCode
) is present in both Pair
and Endpoint
. Also, as I said before, I've added a getPairKeys
method within your Pair
class to get a String
mapping each Pair
and a getFirstPairInitialNumber
method in the CustomPair
class to establish a sorting order among CustomPair
. All the code I've just mentioned can be consulted in the link below. I preferred not to post it here to avoid a wall of text and focusing only the actual solution.
List<CustomPair> listRes = pairs.stream()
.collect(Collectors.groupingBy(Pair::getPairKeys)) //Grouping the Pairs by the keys of the two EndPoints (key1:key2)
.entrySet().stream() //Streaming the entries of the map
.map(entry -> {
String key1 = null, key2 = null;
//Lists for the values of the CustomEndpoint
List<Integer> listValues1 = new ArrayList<>();
List<Integer> listValues2 = new ArrayList<>();
//Retrieving the keys and adding each pair's number to the respective lists
for (Pair p : entry.getValue()) {
key1 = p.getPair().get(0).getKey();
key2 = p.getPair().get(1).getKey();
listValues1.add(p.getPair().get(0).getNumber());
listValues2.add(p.getPair().get(1).getNumber());
}
//Returning the new CustomPair in place of the two grouped by Pair
return new CustomPair(new ArrayList<>(List.of(
new CustomEndpoint(key1, listValues1),
new CustomEndpoint(key2, listValues2))));
})
.sorted(Comparator.comparing(CustomPair::getFirstPairInitialNumber)) //Ordering the CustomPair by the beginning of their endpoint range
.collect(Collectors.toList()); //Collecting the CustomPair
Here is an updated link to test the code