How to merge multiple lists in Java 8 by enum type?
I have 2 lists:
List<StatusSummary> listA //4 items because we have 4 enum types
List<StatusSummary> listB //4 items because we have 4 enum types
class StatusSummary {
private StatusEnum resourcesStatus; //4 statuses: TypeA, TypeB, TypeC, TypeD
private Integer count;
}
I want to have one list consisting of 4 items for each resourcesStatus: TypeA, TypeB, TypeC, TypeD with the sum of elements in count field like:
List<StatusSummary> listA+B with 4 objects:
(resourcesStatus: TypeA, count: 3), (resourcesStatus: TypeB, count:99), (resourcesStatus: typeC, count:322), (resourcesStatus: TypeD, count:2)
So far I implemented something like this:
listA
.getResourcesStatus()
.forEach(la -> listB.getResourcesStatus().stream()
.filter(lb -> lb.getResourcesStatus() == la.getResourcesStatus())
.forEach(lb -> result.add(new StatusSummary()
.count(la.getCount() + lb.getCount())
.status(la.getStatus()))));
You can use Collectors.toMap. Map by ResourcesStatus and merge two StatusSummary with count field in merge-function for the same ResourcesStatus then you get Map<StatusEnum, StatusSummary> and then get values of the map using .values() in ArrayList.
List<StatusSummary> newList =
new ArrayList<>(
Stream.of(listA, listB)
.flatMap(Collection::stream)
.collect(Collectors.toMap(e -> e.getResourcesStatus(), i -> i,
(a,b) -> new StatusSummary(a.getResourcesStatus(),
a.getCount() + b.getCount())))
.values());
Problem using forEach
As far as I see your current solution using forEach, you approach to Stream API as it would be a simple for-loop with additional features. You violate the Side-effects principle of java-stream which in a nutshell says that a stream shouldn't modify another collection while performing the actions through the pipelines. I haven't tested your code, however, this is not a way you should treat streams.
One of the possible solutions
You can use Collectors.groupingBy with Collectors.summingInt to get Map<StatusEnum, Integer> with the status and a sum of counts. Each entry then can be transformed into StatusSummary and colleted as List:
List<StatusSummary> list = Stream.of(listA, listB)
.flatMap(List::stream)
.collect(Collectors.groupingBy( // Map<StatusSummary, Integer>
StatusSummary::getResourcesStatus, // .. StatusEnum as key
Collectors.summingInt(StatusSummary::getCount))) // .. Integer as value (sum)
.entrySet()
.stream() // Stream<Entry<..>>
.map(e -> new StatusSummary(e.getKey(), e.getValue())) // Stream<StatusSummary>
.collect(Collectors.toList()); // List<StatusSummary>
... or using Collectors.collectingAndThen (not less verbose):
List<StatusSummary> list1 = Stream.of(listA, listB, listC)
.flatMap(List::stream)
.collect(Collectors.collectingAndThen(
Collectors.groupingBy(
StatusSummary::getResourcesStatus,
Collectors.summingInt(StatusSummary::getCount)),
map -> map.entrySet().stream()
.map(e -> new StatusSummary(e.getKey(), e.getValue()))
.collect(Collectors.toList())));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With