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?
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.
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.
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.
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.
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)
));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With