javalambdacollectorsreducing

Java Collectors.reducing implementation


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.


Solution

  • The function takes three arguments:

    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 Strings 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 Doubles, 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