reduce补充二

简介: reduce补充二

人生要有意义只有发扬生命,快乐就是发扬生命的最好方法。——张闻天

关于reduce我已经写过博客

今天最后再来聊一聊它的第三个重载

之前一直用得少,所以没有去探究它的妙用

最近稍微抽空看了下

发现还挺有意思的

例如它的第三个参数

并行流的场景下同样的代码竟有不同的效果

如下:

public static void main(String[] args) {
    sum();
    parallelSum();
}
private static void sum() {
    List<Integer> list = Stream.generate(() -> 1).limit(100).collect(Collectors.toList());
    System.out.println(list);
    int sum = list.stream().reduce(1, Integer::sum, (a, b) -> {
        System.out.println("stream执行sum时上次结果:[" + a + "]本次值:[" + b + "]");
        return Integer.sum(a, b);
    });
    System.out.println(sum);
}
private static void parallelSum() {
    List<Integer> list = Stream.generate(() -> 1).limit(100).collect(Collectors.toList());
    System.out.println(list);
    int sum = list.parallelStream().reduce(1, Integer::sum, (a, b) -> {
        System.out.println("parallelStream执行sum时上次结果:[" + a + "]本次值:[" + b + "]");
        return Integer.sum(a, b);
    });
    System.out.println(sum);
}

仅仅是把stream换成了parallelStream,得到的结果就不一样了

并且可以通过日志看出,上方第一个stream是没有执行我们第三个参数BinaryOperator combiner

而我们下面的parallelStream却执行了

并且两者返回的值不一样

第一个返回101

是因为我们调用reduce

给了个默认值为1

再加上我们聚合计算得到的结果为100相加得到101

注意这里我们第三个参数BinaryOperator combiner并没有执行,于是忽略

就像(((默认值1+第一个元素1)+第二个元素1)+第三个元素1)+...这样一直累加,得到结果101

graph LR
普通stream
100个1组成的list -->|第一次累加| 默认值1+第一个元素1 
--> 2 -->|第二次累加| 上次得到的结果2+第二个元素1
--> 省略...

第二个返回了164

是因为我们调用reduce

给了个默认值为1

而我们在并行流计算时,每次计算都会去重复计算一遍这个默认值

就像(默认值1+第一个元素1)+(默认值1+第二个元素1)+(默认值1+第三个元素1)...这样

但要注意,并行流的内部使用了默认的 ForkJoinPool分支/合并框架,它的默认线程数量就是你的处理器数量

我这里最大线程数为64,因此最多我们的默认值会被多加63次,得到结果164,要注意,可能我的最大线程数并没有这么多,一些线程可能会被重复使用,因此累加次数可能是大于最大线程数

它像一个BUG,一个坑,所以要多多注意,这里我们可以避免这个坑的话,初始值我们给0即可,因为0无论是加一次还是63次都不会影响我们最终的结果~

graph LR
并行流parallelStream
list[100个1组成的list] -->|第一次累加| A[默认值1+第一个元素1] --> C[2] --> final[前面的计算执行完毕后]
list --> 注意是同时执行
list -->|第二次累加| B[默认值1+第一个元素1] --> D[2] --> final
list -->|第...次累加| 默认值1+上次得到的结果+第...个元素1 --> E[x] --> final
final --> 将计算结果再次累加得到最后的结果

相关文章
|
资源调度 Java 流计算
Flink on Yarn的两种运行方式
Flink on Yarn的两种运行方式
348 0
|
机器学习/深度学习 算法 数据可视化
Machine Learning机器学习之高维数据降维(主成分分析PCA、线性判别分析、自编码器超级无敌详细讲解)
Machine Learning机器学习之高维数据降维(主成分分析PCA、线性判别分析、自编码器超级无敌详细讲解)
|
12月前
|
JavaScript 前端开发 Java
避免 JavaScript 中的内存泄漏
【10月更文挑战第30天】避免JavaScript中的内存泄漏问题需要开发者对变量引用、事件监听器管理、DOM元素操作以及异步操作等方面有深入的理解和注意。通过遵循良好的编程实践和及时清理不再使用的资源,可以有效地减少内存泄漏的风险,提高JavaScript应用程序的性能和稳定性。
|
8月前
|
存储 设计模式 监控
如何快速定位并优化CPU 与 JVM 内存性能瓶颈?
如何快速定位并优化CPU 与 JVM 内存性能瓶颈?
193 0
如何快速定位并优化CPU 与 JVM 内存性能瓶颈?
|
12月前
|
Web App开发 测试技术 API
Playwright 测试报告中显示的标签和注释。
Playwright 测试报告中显示的标签和注释。
230 57
|
8月前
|
自然语言处理 算法 API
一文揭秘|如何速成RAG+Agent框架大模型应用搭建(一)
一文揭秘|如何速成RAG+Agent框架大模型应用搭建
420 0
|
12月前
|
编译器
R 语言教程 之 R 注释
R语言中的注释仅支持单行注释,使用#符号。多行注释可通过每行添加#或使用if(FALSE){}结构实现。注释帮助理解代码,但不参与执行。示例包括简单的打印语句和两数相加。
363 4
|
编解码 Linux API
从FFplay到自定义播放器:构建高性能多媒体应用程序的进阶之路
【10月更文挑战第15天】多媒体应用程序的开发是一个复杂的过程,尤其是在追求高性能和定制化体验时。本文将引导你从使用FFplay作为起点,逐步过渡到构建一个完全自定义的播放器。我们将探讨FFmpeg库的高级用法、多媒体同步原理、跨平台开发注意事项,以及如何实现用户界面与音视频解码的无缝集成。
380 1
|
缓存 分布式计算 NoSQL
大数据-47 Redis 缓存过期 淘汰删除策略 LRU LFU 基础概念
大数据-47 Redis 缓存过期 淘汰删除策略 LRU LFU 基础概念
231 2
|
缓存 关系型数据库 MySQL
在Linux中,如何优化MySQL性能,包括索引优化和查询分析?
在Linux中,如何优化MySQL性能,包括索引优化和查询分析?