问题描述

问题背景是有一个List<String>你要找到最长的字符串长度,最初的思路就是采用

var ans = buffer.stream()
                .reduce(0, (identity,element)->identity>element.length()?identity:element.length);
        System.out.println(ans);

可是上面的代码就是会报错

按照正常的思路,初始值是0(就是这里的identity),然后接受链表里的元素,然后一路下来比较出最大的字符串长度,思路没有问题啊.于是我尝试了一下换了

var ans = buffer.stream()
                .reduce(0, (identity, element) -> element.length() > identity ? element.length() : identity, Math::max);
        System.out.println(ans);

这下就完全是对了

问题答案

其实这个问题,我查了下SO,发现有相应的回答,我先把原答案贴出来吧

为什么reduce的时候需要combiner

我在这简便记录一下,做下总结

自我总结

  • 为什么reduce 需要combiner
  • reduce 是个什么样的过程
  • reduce 怎么用

问题回答

reduce() 有两种,一种是

T reduce(T identity,
         BinaryOperator<T> accumulator)

另一种是

<U> U reduce(U identity,
             BiFunction<U,? super T,U> accumulator,
             BinaryOperator<U> combiner)

分别对应上边报错和正确的解法,我们的目的是把String reduce成 Integer,对应两种不同的类型,上面的那种就是reduce成相同类型,所以我们这里需要一个combiner,这个方面完全就是从调用角度上来看的,没有啥太大意义,正如我刚才所说的,按照正常的思路完全可以实现,就像这样

java stream map相加 java stream map reduce_debug

那为什么java 库硬要搞出来一个combiner.这是因为

one of the design principles of the Streams API is that the API shouldn’t differ between sequential and parallel streams, or put another way, a particular API shouldn’t prevent a stream from running correctly either sequentially or in parallel. If your lambdas have the right properties (associative, non-interfering, etc.) a stream run sequentially or in parallel should give the same results.

简单来说就是并行和串行要准守同一套设计原则,不能我专门设计一个方法给你串行,然后我的并行没法用,反之同理.所以我们要看看,并行是如何进行reduce 的,拿一下原答案的图

java stream map相加 java stream map reduce_java_02

一图胜千言,图解释的很清楚了,没啥要说了.
nuget:

  • 之后想要reduce成另外一种类型,就得注意combiner了,可以注意并行流操作,并行流速度快一点
  • 还是建议先map 或者其他操作转化成相同的类型在处理.比如maptoInt()转化成字符串的长度

配上相应的reduce教程

https://www.baeldung.com/java-stream-reduce

看完reduce ,打算再看看Collector,groupingBy 啥的,嗯,妙~~~~啊