337. Java Stream API - 理解 Java S

2026-02-27  本文已影响0人  Cache技术分享

337. Java Stream API - 理解 Java Stream 中的并行拆分


🎯 为什么要拆分?

在使用 parallelStream() 时,Java 会尝试将数据源拆分成多个子任务并在多个 CPU 核心上并行处理。

因此,数据能否有效拆分,是决定并行流性能的关键因素之一!


🧩 拆分的三大标准

一个“适合拆分”的数据源,应具备:

  1. 拆得快:可以高效找到中间点
  2. 拆得平:能平均分配处理负载
  3. 可预测:能预估总量及子部分数据量

📦 常见集合的拆分能力分析

1️⃣ ArrayList —— 拆得快 & 拆得平 ✅✅✅

List<Integer> list = IntStream.range(0, 1_000_000).boxed().toList();
List<Integer> sub1 = list.subList(0, 500_000);
List<Integer> sub2 = list.subList(500_000, 1_000_000);

🧠 类比:像切蛋糕,一刀下去刚好一半!


2️⃣ LinkedList —— 拆得慢 ⚠️

LinkedList<Integer> list = new LinkedList<>();
IntStream.range(0, 1_000_000).forEach(list::add);

🧠 类比:像找书架中间那本书,但书只能一本本翻,效率极低。


3️⃣ HashSet —— 拆分难度中等 ⚠️

🔍 拆分时难以平均切分工作量


4️⃣ TreeSet —— 拆得平,但有指针跳转 ⚠️✅

🧠 类比:像拆一个大树枝成两个分支,但分支里的果子不在一起。


📄 非集合数据的拆分挑战

📄 Files.lines(path) —— 拆不了

Files.lines(Paths.get("data.txt"))
     .parallel()
     .forEach(System.out::println);

📍 Pattern.splitAsStream() —— 拆不了

Pattern.compile(",").splitAsStream("a,b,c,d,e")
       .parallel() // 实际并无拆分优势
       .forEach(System.out::println);

📐 范例对比:可拆与不可拆的生成流

✅ 可拆分的 IntStream.range

List<Integer> list1 = IntStream.range(0, 10)
                               .boxed()
                               .toList();

❌ 不易拆分的 IntStream.iterate

List<Integer> list2 = IntStream.iterate(0, i -> i + 1)
                               .limit(10)
                               .boxed()
                               .toList();

🧠 类比:像按公式生成每个步骤,不能直接跳到中间。


🔍 总结表:数据源拆分能力一览

数据源/结构 是否易拆 是否平均 是否适合并行流
ArrayList ✅ 快速 ✅ 平均 ✅ 非常适合
LinkedList ❌ 慢 ✅ 理论可平均 ⚠️ 不建议使用
HashSet ✅ 快速 ❌ 分布不均 ⚠️ 有风险
TreeSet ✅ 可拆 ✅ 平衡 ⚠️ 有指针追踪
Files.lines() ❌ 无法预测 ❌ 无法分块 ❌ 仅适合串行
Pattern.splitAsStream() ❌ 不可控 ❌ 不可控 ❌ 不推荐
IntStream.range() ✅ 易拆 ✅ 平均 ✅ 高性能
IntStream.iterate() ❌ 连锁依赖 ❌ 不均 ❌ 慢且不可控

🚫 常见误区提示


✅ 最佳实践建议

上一篇 下一篇

猜你喜欢

热点阅读