Sum attributes of an element in a stream

Viewed 141

I want to summarize the attributes of my objects. The objects have a structure like this:

class Foo{
   String name;
   int = price;
   int = fees;
   //etc
}

I have collected them into a List and want to get (with streams) a list of all elements that only contain the name and the sum of the price and fees. For example:

{name=Apple, price=2; fees=1}
{name=Potato, price=7, fees=10}
{name=strawberry, price=5, fees=4}

should lead to this result:

{name=Apple, sum=3}
{name=Potato, sum=17}
{name=strawberry, sum=9}
2 Answers
Map<String, Integer> result = 
foos.stream()
    .collect(
        Collectors.groupingBy(
            Foo::getName, 
            Collectors.summingInt(x -> x.getPrice() + x.getFees())
        )
);

collect to a Map with a Collectors::groupingBy and a downstream collector : Collectors::summingInt.

Or you can use toMap:

foos.stream()
    .collect(
        Collectors.toMap(
               Foo::getName,
               x -> x.getPrice() + x.getFees(),
               Integer::sum
        )
    );

This is rather easy to achieve if you just read the documentation a bit.

Implement class Bar:

class Bar {
    String name;
    int sum;

    // mapping constructor
    public Bar(Foo foo) {
        this.name = foo.name;
        this.sum = foo.price + foo.fees;
    }
}

and remap Foo using reference to the constructor:

List<Bar> sums = listFoos.stream().map(Bar::new).collect(Collectors.toList());

Instead of constructor a method with the same functionality may be implemented.


If multiple instances of Foo need to be summarized into single Bar, a method to merge a Bar may be added to Bar:

class Bar {
// ...
    public Bar merge(Bar bar) {
        this.sum += bar.sum;
        return this;
    }
}

Then the collection of Bar can be retrieved as values of the following map:

Collection<Bar> sumBars = listFoos
    .stream()
    .collect(Collectors.toMap(Foo::getName, Bar::new, Bar::merge))
    .values();
Related