Java Collectors.reducing 实现

dev*_*evv -2 java lambda reducing collectors

int val = integerList.stream().collect(
        Collectors.reducing(0, a1 -> a1 * 5, (a1, a2) -> a1 + a2));
Run Code Online (Sandbox Code Playgroud)

上面的代码进行了归约操作。转换整数流和聚合函数以返回 Integer 。我无法理解以下代码和归约操作的内部实现。Java 如何执行以下有状态功能?谢谢!

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);
    }
Run Code Online (Sandbox Code Playgroud)

可能是,我会更好地澄清我的问题。上面的实现是如何获取数据流的。a[0],b[0] 是指数据流吗?我相信以上是为供应商和累加器提供功能实现。我想通过代码了解缩减过程是如何工作的。

Pio*_*kin 6

该函数接受三个参数:

  • 第一个是身份。减少流时,您必须从某个地方开始(否则,减少空列表的结果是什么?)。标识是应用于链中第一个归约操作的第一个参数的对象

  • 第二个是映射器。reducing()是一个广义操作 - 您可以将 type 流的元素减少为 typeT的最终结果U,因此您必须提供一个中间操作,该操作U从 typeT元素提供type元素。如果T == U您不想进行转换,则可以在此处提供标识功能

  • 第三个参数是归约函数 - 这是从标识开始按顺序应用于流元素的函数

所以,作为一个例子:

如果您只想将Integer流的元素汇总为一个整数,则可以使用Collectors.reducing(0, x -> x, (x, y) -> x + y).

如果要汇总流中Strings的长度String,可以使用Collectors.reducing(0, String::length, (x, y) -> x + y).

如果你想Double从一串Doubles 中得到最大值,但不小于Math.PI,你可以使用Collectors.reducing(Math.PI, x -> x, Math::max).

另外,如果你希望你的reduce是有状态的,请记住你可以将一个对象内部方法的引用用作reducor。这样,对象就可以用来保持状态。例如,这是一个“减税器”,每增加 100 次,它的分数就会增加 1 次“税”:

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);
Run Code Online (Sandbox Code Playgroud)

同样可以扩展使用来实现复杂的情况,如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)));
Run Code Online (Sandbox Code Playgroud)

这里首先输入字符串根据它们开始的字符分组,然后长度相加