Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8 GroupingBy with Collectors on an object

I would like to stream on a collection of object myClass in order to grouping it using Collectors.groupingBy(). But, instead of retrieving a Map<String, List<myClass>>, I would like to group it on a object myOutput and retrieve a Map<String, myOutput>. I tried to create a custom collector :

List<myClass> myList = new ArrayList<myClass>();
myList.add(new myClass("a", 1));
myList.add(new myClass("a", 2));
myList.add(new myClass("b", 3));
myList.add(new myClass("b", 4));

Map<String,myOutput> myMap = myList.stream().collect(Collectors.groupingBy(myClass::getA, Collectors.of(myOutput::new, myOutput::accept, myOutput::combine)));

myClass :

protected String a;
protected int b;

public myClass(String aA, int aB)
{
  a = aA;
  b = aB;
}

public String getA()
{
  return a;
}

public int getB()
{
  return b;
}

myOutput :

protected int i;

public myOutput()
{
  i = 0;
}

public void accept(myClass aMyClass)
{
  i += aMyClass.getB();
}

public myOutput combine(myOutput aMyOutput)
{
  i += aMyOutput.getI();
  return this;
}

public int getI()
{
  return i;
}

But with this code, there is a problem with the collector :

Collectors.of(myOutput::new, myOutput::accept, myOutput::combine)

I know in this case a reduction will be much easier, but let's assume there are a lot of operation to do in the myOutput object.

What's wrong with this collector?

like image 543
Antoine Millet Avatar asked Mar 22 '16 15:03

Antoine Millet


People also ask

How do you use groupingBy collectors?

The groupingBy() method of Collectors class in Java are used for grouping objects by some property and storing results in a Map instance. In order to use it, we always need to specify a property by which the grouping would be performed. This method provides similar functionality to SQL's GROUP BY clause.

What does collect collectors toList ()) do?

The toList() method of Collectors Class is a static (class) method. It returns a Collector Interface that gathers the input data onto a new list. This method never guarantees type, mutability, serializability, or thread-safety of the returned list but for more control toCollection(Supplier) method can be used.

What is the use of groupingBy in Java 8?

groupingBy() method in Java 8 now permits developers to perform GROUP BY operation directly. GROUP BY is a SQL aggregate operation that is quite useful. It enables you to categorise records based on specified criteria.

What is a downstream collector?

The term downstream in the documentation refers to one Collector accepting a second Collector as an argument. The argument is applied downstream (after) the Collector that accepts it. In other words, the downstream Collector is applied to the result of the upstream Collector. In your example, Collectors.


1 Answers

Your collector is fine. You just need to have the Collector.of static factory (and not Collectors.of).

This compiles fine and has the output you want

    Map<String,myOutput> myMap = 
        myList.stream()
              .collect(Collectors.groupingBy(
                myClass::getA, 
                Collector.of(myOutput::new, myOutput::accept, myOutput::combine)
              ));

Note, however, that you don't need such a collector. You can reuse an existing one. In this case, you want to group by the a value and for each element grouped to the same a, you want to sum their b value. You can use the built-in Collectors.summingInt(mapper) where the mapper returns the b value:

Map<String,Integer> myMap = 
    myList.stream()
          .collect(Collectors.groupingBy(
            myClass::getA, 
            Collectors.summingInt(myClass::getB)
          ));
like image 128
Tunaki Avatar answered Oct 30 '22 14:10

Tunaki