Java8集合list.parallelStream() 和 list.stream() 区别

list.parallelStream()list.stream() 的主要区别在于并行处理能力

1. 核心区别

  • list.stream():顺序流,单线程顺序处理

  • list.parallelStream():并行流,自动使用多线程并行处理

2. 执行方式对比

java 复制代码
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

// 顺序流 - 单线程顺序执行
numbers.stream()
       .map(n -> {
           System.out.println(Thread.currentThread().getName() + ": " + n);
           return n * 2;
       })
       .forEach(System.out::println);

// 输出(大致):
// main: 1
// 2
// main: 2
// 4
// main: 3
// 6
// 并行流 - 多线程并行执行
numbers.parallelStream()
       .map(n -> {
           System.out.println(Thread.currentThread().getName() + ": " + n);
           return n * 2;
       })
       .forEach(System.out::println);

// 输出(可能):
// ForkJoinPool.commonPool-worker-1: 3
// ForkJoinPool.commonPool-worker-2: 2
// main: 1
// 6
// 4
// 2
// ...

3. 性能特点

特性 stream() parallelStream()
线程使用 单线程 ForkJoinPool 线程池
数据顺序 保持顺序 可能乱序(除非用forEachOrdered
适用场景 小数据量、顺序敏感操作 大数据量、CPU密集型计算
开销 有线程创建、同步开销

4.使用场景建议

适合使用 stream() 的情况:

java 复制代码
// 1. 数据量小
List<String> smallList = ...;
smallList.stream()...

// 2. 需要保持顺序
list.stream()
    .sorted()           // 排序操作
    .forEach(...);      // 顺序重要

// 3. I/O密集型操作(文件读写、网络请求)
list.stream()
    .map(this::readFromFile)  // I/O操作通常不适合并行
    .collect(Collectors.toList());

// 4. 有状态操作
list.stream()
    .skip(2)      // 跳过前两个
    .limit(10)    // 限制数量
    .collect(...);

适合使用 parallelStream() 的情况

java 复制代码
// 1. 大数据集(通常 > 10000个元素)
List<Integer> largeList = ...;
largeList.parallelStream()
         .map(x -> heavyCalculation(x))  // 耗时计算
         .collect(Collectors.toList());

// 2. CPU密集型计算
list.parallelStream()
    .map(this::complexMathOperation)  // 复杂数学运算
    .reduce(0, Integer::sum);

// 3. 操作之间无依赖
list.parallelStream()
    .filter(this::isValid)      // 过滤可以并行
    .map(this::transform)       // 映射可以并行
    .collect(Collectors.toList());

5. 重要注意事项

线程安全问题

java 复制代码
List<Integer> result = Collections.synchronizedList(new ArrayList<>());

// ❌ 错误:并行修改共享状态
List<Integer> unsafeList = new ArrayList<>();
list.parallelStream()
    .forEach(unsafeList::add);  // 可能抛出ConcurrentModificationException

// ✅ 正确:使用线程安全集合或避免共享状态
list.parallelStream()
    .collect(Collectors.toList());  // 使用内置收集器

顺序保证:

java 复制代码
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);

// 顺序不确定
list.parallelStream()
    .forEach(System.out::print);  // 可能输出:3 1 4 2 5

// 保持顺序
list.parallelStream()
    .forEachOrdered(System.out::print);  // 总是输出:1 2 3 4 5

6. 性能测试示例

java 复制代码
public class StreamBenchmark {
    public static void main(String[] args) {
        List<Integer> numbers = IntStream.rangeClosed(1, 1_000_0000)
                                         .boxed()
                                         .collect(Collectors.toList());
        
        // 顺序流
        long start = System.currentTimeMillis();
        long sum1 = numbers.stream()
                          .mapToLong(i -> (long)i * i)
                          .sum();
        long time1 = System.currentTimeMillis() - start;
        
        // 并行流
        start = System.currentTimeMillis();
        long sum2 = numbers.parallelStream()
                          .mapToLong(i -> (long)i * i)
                          .sum();
        long time2 = System.currentTimeMillis() - start;
        
        System.out.println("顺序流时间: " + time1 + "ms");
        System.out.println("并行流时间: " + time2 + "ms");
        System.out.println("结果一致: " + (sum1 == sum2));
    }
}

7. 最佳实践建议

  1. 先测试,再选择:对于关键代码,先测试两种方式的性能

  2. 默认用顺序流:除非有明确证据表明并行流更好

  3. 考虑NQ模型:只有当 N(数据量) × Q(每个元素计算量)较大时,并行才有效

  4. 避免副作用:并行流中避免修改外部状态

  5. 注意资源限制:并行流使用公共ForkJoinPool,可能影响其他并行任务

总结

  • stream():简单、可预测、适用于大多数场景

  • parallelStream():性能可能更好,但需要满足特定条件,且要注意线程安全和顺序问题

简单选择原则 :默认使用 stream(),只有当处理大数据集且操作耗时的情况下,才考虑使用 parallelStream() 并进行充分测试。

相关推荐
八解毒剂3 分钟前
数据结构-平衡二叉树——对二叉搜索树的优化
数据结构·c++·算法
Darling噜啦啦2 小时前
JavaScript 数组深度解析:从纯函数到二维数组陷阱,一文吃透前端数据结构核心
前端·javascript·数据结构
不会就选b3 小时前
数据结构之链表OJ题(中)
数据结构·链表
J2虾虾3 小时前
C语言 typedef 用法
c语言·数据结构·算法
budingxiaomoli4 小时前
二叉树中的深搜
数据结构
断点之下4 小时前
数据结构从零开始④:堆——一种特殊的完全二叉树(附堆排序、TopK问题)
数据结构
WL学习笔记4 小时前
顺序表详解
c语言·数据结构
sugar__salt4 小时前
深入吃透前端线性数据结构:数组、栈、队列、链表核心原理与实战
前端·数据结构·链表
8Qi85 小时前
LeetCode 32:最长有效括号 —— 栈 + 标记法 题解
java·数据结构·算法·leetcode·职场和发展··括号匹配
洛水水5 小时前
【力扣100题】76.搜索插入位置
数据结构·算法·leetcode