How can I get some statistics using java 8 stream with object list

776 Views Asked by At

I have a List<SqlResult>. I need to get a List<Result> from the aggregation of SqlResult. For example,

SqlResult(Input)

public class SqlResult{
  private String key1;
  private String key2;
  private Double val;
  // getters, setters and constructor

Result Data Class

public class Result{
  private String key1;
  private String key2;
  private Double avgVal;
  private Long   count;
  // getters, setters and constructor

I want get List like below, group by with key1, key2, count of members(count), average of val(avgVal) Below code throw NullPointerException.

public class Main 
{
  public static void main(String[] args) {
    List<SqlResult> listSqlResult = new ArrayList<>();
    listSqlResult.add(new SqlResult(a1,b1,123));
    listSqlResult.add(new SqlResult(a1,b1,10));
    listSqlResult.add(new SqlResult(a1,b1,23));
    listSqlResult.add(new SqlResult(a1,b2,3));
    listSqlResult.add(new SqlResult(a1,b2,73));
    listSqlResult.add(new SqlResult(a1,b2,15));
    listSqlResult.add(new SqlResult(a2,b1,43));
    listSqlResult.add(new SqlResult(a2,b1,19));
    listSqlResult.add(new SqlResult(a2,b1,15));
    listSqlResult.add(new SqlResult(a2,b2,38));
    listSqlResult.add(new SqlResult(a2,b2,73));
    listSqlResult.add(new SqlResult(a2,b2,15));

    List<Result> listResult = listSqlResult.stream
        .collect(groupingBy(SqlResult::getKey1,
                 groupingBy(SqlResult::getKey2))).values().stream()
        .map(e -> new Result(e.get(0).get(0).getKey1(), e.get(0).get(0).getKey2(),e.get(0).stream().mapToDouble(e::getValue).average(), e.get(0).stream().count()))
    .collect(Collectors.toList())
;

How can I get statistical results (average of val, count of member) using Java Stream API?

1

There are 1 best solutions below

0
On BEST ANSWER

The cause for your code failing is that you end up performing a Map.get with Integer key over a Map which during groupingBy was constructed with Strings in keyset. Hence the corresponding null value in lookup followed by an NPE.

It would be simpler if you could think of formulating your requirements into two steps. One, where you summarise the data that is in your hand. Other, where you map it to the desired result object of your choice.

With the help of existing Collectors.summarizing..., this could look as simple as the following:

Map<List<String>, DoubleSummaryStatistics> groupedStatistics = listSqlResult.stream()
        .collect(Collectors.groupingBy(sr -> Arrays.asList(sr.getKey1(), sr.getKey2()),
                Collectors.summarizingDouble(SqlResult::getVal)));

List<Result> results = groupedStatistics.entrySet().stream()
        .map(e -> new Result(e.getKey().get(0), e.getKey().get(1),
                e.getValue().getAverage(), e.getValue().getCount()))
        .collect(Collectors.toList());