int val = integerList.stream().collect(
Collectors.reducing(0, a1 -> a1 * 5, (a1, a2) -> a1 + a2));
The above code does the reduction operation.Transforming the stream of integers and the aggregation function to return Integer . I couldn't comprehend the below code and the internal implementation of the reduction operation.How Java could perform the below Stateful function? Thank you!
java.util.stream.Collectors:reducing method
public static <T, U>
Collector<T, ?, U> reducing(U identity,
Function<? super T, ? extends U> mapper,
BinaryOperator<U> op) {
return new CollectorImpl<>(
boxSupplier(identity),
(a, t) -> { a[0] = op.apply(a[0], mapper.apply(t)); },
(a, b) -> { a[0] = op.apply(a[0], b[0]); return a; },
a -> a[0], CH_NOID);
}
May be, I will clarify my question little better. How the above implementation gets the stream of data. Does a[0],b[0] refers the Stream of data? I believe the above is providing functional implementation to supplier and accumulator. I wanted to understand how the reduction process works through the code.
The function takes three arguments:
The first one is an identity. When reducing a stream, you have to start somewhere (otherwise, what would be the result of reducing an empty list?). The identity is the object applied to the first argument of the first reduction operation in the chain
The second one is a mapper. reducing()
is a generalized operation - you can reduce elements of a stream of type T
into a final result of type U
, so you have to give an intermediate operation that provides a type U
element from a type T
element. If T == U
and you don't want a transformation, you can provide an identity function here
The third argument is the reduction function - this is the one applied to elements of the stream in sequence, starting from the identity
So, as an example:
If you want to just summarize the elements of an Integer
stream into an integer, you could use Collectors.reducing(0, x -> x, (x, y) -> x + y)
.
If you want to summarize the lengths of String
s in a String
stream, you could use Collectors.reducing(0, String::length, (x, y) -> x + y)
.
If you want to get the maximum Double
from a string of Double
s, but no less than Math.PI
, you could use Collectors.reducing(Math.PI, x -> x, Math::max)
.
Also, if you want your reduction to be stateful, remember that you can use as a reductor a reference to a method inside an object. That way, the object can be used to keep the state. For example, here's a "taxing reductor" that adds 1 "tax" to its score every 100 additions:
public class Taxer implements BinaryOperator<Integer> {
int counter = 0;
@Override
public Integer apply(Integer i1, Integer i2) {
counter++;
if (counter % 100 == 0) {
return i1 + i2 + 1;
} else {
return i1 + i2;
}
}
}
...
Taxer t = new Taxer();
...
.collect(Collectors.reducing(0, x -> x, t);
The same can be extended using to implement complex cases like groupingBy
:
Map<String, Integer> output = Stream.of("this", "word", "is", "the", "best")
.collect(Collectors.groupingBy(x-> x.substring(0, 1),
Collectors.reducing(0, x-> x.length(), (x, y)-> x + y)));
Here first the input string is grouped based on the character they start with and then the lengths are summed