horus
horus

Reputation: 115

Java 8 - How to sum all List element in a Map of Lists

I have a Map of Lists of a particular Object and I want to add all the elements of the lists based on a particular instance variable.

My Object:

class Risk{ 
    Integer id, riskValue, totRisk=0;
    String name;

    Integer getId(){
       return id;
    }
    Risk(Object[] result){
         id=(Integer)result[0];
         riskValue=(Integer)result[1];
         name=(String)result[3];
    }
}

I get from the Database a List of Arrays of type object:

List< Object[] > results=getResults();

I use Java 8 to group my Data by ID, because I want to SUM by riskValue all the objects of type Risk having the same id.

This is what I do:

List<Risk> risks=results.stream().map(Risk::new).collect(Collectors.toList());
Map<Integer, List<Risk>> byId=risks.stream.collect(Collectors.groupingBy(Risk::getId));

At this point I have all my Risk objects grouped by ID. And I want for each list to sum all the objects by riskValue and have the total in the variable totRisk

How to compute in the variable totRisk the total of the variables riskValue in each List?

Note1: I want to do it using Java 8, I know how to do it using Java 7 and below.

Note2: Perhaps it's also possible to do it in one go, by not having first to group by ID. What I want to achieve is to sum all the objects with the same ID in the original List<Object[]> results. If it can be done with only one statement is even better.

Upvotes: 7

Views: 11168

Answers (1)

Holger
Holger

Reputation: 298143

You have to be aware that you can combine Collectors. See Collectors.groupingBy(Function,Collector)

Map<Integer, Integer> byId=risks.stream.collect(
    Collectors.groupingBy(Risk::getId, Collectors.summingInt(Risk::getRiskValue)));

You can also combine it with the first operation:

Map<Integer, Integer> byId=results.stream().map(Risk::new).collect(
    Collectors.groupingBy(Risk::getId, Collectors.summingInt(Risk::getRiskValue)));

Note that I assume that you have a method getRiskValue() in your class Risk, otherwise you have to replace Risk::getRiskValue with a lambda expression r -> r.riskValue to access the field, however, having getter methods is always recommended.

The result maps from id to total.


After reading your question again, I noticed that you actually want to sum up riskValue and store it within totRisk of each (?) Risk instance. This is a bit more complicated as it doesn’t fit the common usage pattern:

Map<Integer, List<Risk>> byId=results.stream().map(Risk::new).collect(
  Collectors.groupingBy(Risk::getId, Collectors.collectingAndThen(
    Collectors.toList(), l-> {
      int total=l.stream().collect(Collectors.summingInt(r -> r.riskValue));
      l.forEach(r->r.totRisk=total);
      return l;
    })));

at this point we really should switch to using import static java.util.stream.Collectors.*;:

Map<Integer, List<Risk>> byId=results.stream().map(Risk::new).collect(
  groupingBy(Risk::getId, collectingAndThen(toList(), l-> {
    int total=l.stream().collect(summingInt(r -> r.riskValue));
    l.forEach(r->r.totRisk=total);
    return l;
  })));

Upvotes: 10

Related Questions