Antoine Millet
Antoine Millet

Reputation: 53

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?

Upvotes: 5

Views: 3073

Answers (1)

Tunaki
Tunaki

Reputation: 137084

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)
          ));

Upvotes: 6

Related Questions