Reputation: 11799
Is there a concise way to sum the units of the same type of items in a list with Java 8 streams? For example, suppose I have a list of three items:
{id: 10, units: 1}
{id: 20, units: 2}
{id: 10, units: 1}
I like a summary stream like:
{id: 10, units 2}
{id: 20, units 2}
which sums the units of items of the same id. Any ideas?
Here's Federico's solution (with Lombok):
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class So44348207 {
public static void main(String[] args) {
List<Item> items = Arrays.asList(
new Item(10,1), new Item(20,2), new Item(10, 1)
);
Map<Long, Integer> results = items.stream().collect(
Collectors.toMap(
Item::getId,
Item::getUnits,
Integer::sum
)
);
results.forEach( (k,v) -> System.out.println(
String.format("{id: %d, units: %d}", k,v))
);
}
@Data
@AllArgsContructor
public static class Item {
Long id;
Integer units;
}
}
which correctly produces:
java So44348207
{id: 20, units: 2}
{id: 10, units: 2}
Upvotes: 4
Views: 1745
Reputation: 1254
Here is another simple solution by StreamEx
StreamEx.of(items).toMap(Item::getId, Item::getUnits, Integer::sum);
Upvotes: 1
Reputation: 34460
Assuming you have a class MyClass
that encapsulates both fields and has getters for them, you could do it as follows:
Map<Long, Integer> result = list.stream()
.collect(Collectors.toMap(
MyClass::getId,
MyClass::getUnits,
Integer::sum));
I'm also assuming that id
is of type long
or Long
and units
of type int
or Integer
.
Collectors.toMap
creates a map whose keys are determined by its first argument, which is a function that maps an element of the stream to the key of the map. The second argument is a function that maps an element of the stream to the value of the map, and the third argument is an operator that merges two values when there are collisions on the key.
Upvotes: 5