Java 8 Stream API 完全指南:优雅处理集合数据

"Stream API 是Java 8最革命性的改变,它让Java拥有了现代化的数据处理能力" ------ Martin Fowler

一、Stream API 是什么?

Stream API 是Java 8引入的一套函数式数据处理工具,它允许你以声明式方式处理集合数据。与传统循环相比,Stream API 更简洁、更易读,并且能轻松利用多核架构。

核心特点

  • 非数据结构:不存储数据,只定义操作

  • 不修改源数据:所有操作返回新Stream

  • 惰性执行:终端操作触发实际计算

  • 可并行化:parallel()开启并行处理

二、Stream 操作分类

操作类型 特点 常见方法
创建操作 创建Stream stream(), of(), generate(), iterate()
中间操作 定义处理链 filter(), map(), flatMap(), distinct(), sorted(), limit()
终端操作 触发计算 forEach(), collect(), reduce(), count(), anyMatch()

三、创建 Stream 的 7 种方式

java 复制代码
// 1. 从集合创建
List<String> list = Arrays.asList("Java", "Python", "C++");
Stream<String> stream1 = list.stream();

// 2. 从数组创建
String[] array = {"Apple", "Banana", "Orange"};
Stream<String> stream2 = Arrays.stream(array);

// 3. 使用Stream.of()
Stream<String> stream3 = Stream.of("A", "B", "C");

// 4. 生成常量流
Stream<String> constStream = Stream.generate(() -> "Hello").limit(5);

// 5. 生成数列
Stream<Integer> numStream = Stream.iterate(0, n -> n + 2).limit(10);

// 6. 从文件创建
Path path = Paths.get("data.txt");
Stream<String> fileStream = Files.lines(path);

// 7. 基本类型流(避免装箱开销)
IntStream intStream = IntStream.range(1, 100);

四、核心中间操作详解

1. 过滤 - filter()
java 复制代码
// 保留长度大于4的字符串
List<String> longNames = list.stream()
    .filter(s -> s.length() > 4)
    .collect(Collectors.toList());
2. 映射 - map()
java 复制代码
// 将字符串转为大写
List<String> upperCase = list.stream()
    .map(String::toUpperCase)
    .collect(Collectors.toList());
3. 扁平化 - flatMap()
java 复制代码
// 合并多个集合
List<List<String>> nestedList = ...;
List<String> flatList = nestedList.stream()
    .flatMap(Collection::stream)
    .collect(Collectors.toList());
4. 去重 - distinct()
java 复制代码
List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 3, 3);
List<Integer> unique = numbers.stream()
    .distinct()
    .collect(Collectors.toList()); // [1, 2, 3]
5. 排序 - sorted()
java 复制代码
// 按长度排序
List<String> sorted = list.stream()
    .sorted(Comparator.comparingInt(String::length))
    .collect(Collectors.toList());

五、终端操作实战

1. 收集结果 - collect()
java 复制代码
// 转为List
List<String> resultList = stream.collect(Collectors.toList());

// 转为Set
Set<String> resultSet = stream.collect(Collectors.toSet());

// 转为Map
Map<String, Integer> nameMap = list.stream()
    .collect(Collectors.toMap(
        Function.identity(), // 键
        String::length       // 值
    ));

// 分组
Map<Integer, List<String>> groupByLength = list.stream()
    .collect(Collectors.groupingBy(String::length));

// 分区(按条件分组)
Map<Boolean, List<String>> partition = list.stream()
    .collect(Collectors.partitioningBy(s -> s.length() > 4));
2. 聚合计算 - reduce()
java 复制代码
// 求和
int sum = IntStream.range(1, 10).reduce(0, (a, b) -> a + b);

// 连接字符串
String concat = list.stream()
    .reduce("", (a, b) -> a + "," + b);
3. 匹配检查
java 复制代码
boolean allLong = list.stream().allMatch(s -> s.length() > 3); // 所有元素满足条件
boolean anyLong = list.stream().anyMatch(s -> s.length() > 5); // 任一元素满足条件
boolean noneEmpty = list.stream().noneMatch(String::isEmpty); // 没有空字符串
4. 查找元素
java 复制代码
Optional<String> firstLong = list.stream()
    .filter(s -> s.length() > 5)
    .findFirst(); // 获取第一个

Optional<String> anyLong = list.stream()
    .filter(s -> s.length() > 5)
    .findAny(); // 获取任意一个(并行流更高效)

六、并行流:释放多核威力

java 复制代码
// 顺序流
long start = System.currentTimeMillis();
list.stream().forEach(...);
long seqTime = System.currentTimeMillis() - start;

// 并行流
start = System.currentTimeMillis();
list.parallelStream().forEach(...);
long parTime = System.currentTimeMillis() - start;

System.out.println("并行加速比:" + (double)seqTime/parTime);

使用建议

  • 数据量 > 10000 时效果明显

  • 避免共享可变状态

  • 操作应独立无状态

  • 避免I/O密集型操作

七、实战案例:电商数据分析

java 复制代码
List<Product> products = Arrays.asList(
    new Product("Laptop", 1200, "Electronics"),
    new Product("Shirt", 50, "Fashion"),
    new Product("Phone", 800, "Electronics"),
    new Product("Book", 20, "Education")
);

// 案例1:计算电子类产品平均价格
double avgPrice = products.stream()
    .filter(p -> "Electronics".equals(p.getCategory()))
    .mapToDouble(Product::getPrice)
    .average()
    .orElse(0.0);

// 案例2:按类别分组并统计
Map<String, DoubleSummaryStatistics> statsByCategory = products.stream()
    .collect(Collectors.groupingBy(
        Product::getCategory,
        Collectors.summarizingDouble(Product::getPrice)
    ));

// 案例3:获取最贵的3个产品
List<Product> top3 = products.stream()
    .sorted(Comparator.comparingDouble(Product::getPrice).reversed())
    .limit(3)
    .collect(Collectors.toList());

// 案例4:并行处理百万数据
List<Product> bigData = // 百万级数据
Map<String, List<Product>> categoryMap = bigData.parallelStream()
    .collect(Collectors.groupingByConcurrent(Product::getCategory));

八、性能优化指南

  1. 避免装箱开销:使用IntStream/LongStream/DoubleStream

    java 复制代码
    // 低效
    list.stream().mapToInt(s -> s.length()).sum();
    
    // 高效
    list.stream().mapToInt(String::length).sum();

合并操作 :减少中间操作数量

java 复制代码
// 优化前:两次遍历
long count = list.stream().filter(s -> s.length() > 3).count();

// 优化后:一次遍历
boolean exists = list.stream().anyMatch(s -> s.length() > 3);
  1. 短路操作优先:findFirst/anyMatch代替collect

    java 复制代码
    // 找到第一个即停止
    Optional<String> result = list.stream()
         .filter(s -> s.contains("error"))
         .findFirst();
  2. 并行流注意点

    • 小数据量(<1000)不要用

    • 避免共享状态

    • 确保操作可并行化

九、Stream vs 传统循环对比

场景 传统循环 Stream API
简单遍历 ✅ 更直接 ⚠️ 稍重
复杂数据处理 ❌ 嵌套难读 ✅ 声明式清晰
并行处理 ❌ 复杂易错 ✅ 一行代码
延迟处理 ❌ 立即执行 ✅ 惰性求值
代码复用 ❌ 困难 ✅ 管道组合

十、最佳实践总结

  1. 保持简洁:单个Stream操作不超过5个步骤

  2. 避免副作用:不要在lambda中修改外部状态

  3. 优先使用方法引用String::lengths -> s.length() 更简洁

  4. 合理使用Optional:安全处理可能为空的结果

  5. 适时使用并行:大数据集才值得

黄金法则:Stream API 不是要完全取代循环,而是在处理复杂数据管道时提供更优雅的解决方案。

动手练习:使用Stream API实现以下功能

// 给定数字列表:1, 2, 3, 4, 5, 6, 7, 8, 9

// 1. 找出所有偶数

// 2. 每个数字平方

// 3. 忽略前2个结果

// 4. 计算剩余数字的平均值

相关推荐
AAA修煤气灶刘哥几秒前
手把手教你Mybatis-Plus :小白看完都能会,看完还不回找我,我给你补个蛋
java·后端
悦人楼4 分钟前
当C#遇上Notepad++:实现GCode可视化编辑的跨界实践
java·c#·notepad++
Kyln.Wu4 分钟前
【python实用小脚本-182】Python一键爬取今日新闻:5分钟生成Word+CSV——再也不用复制粘贴
开发语言·python·word
沐知全栈开发17 分钟前
《C 指针》深度解析
开发语言
bug菌33 分钟前
🤔当类被注解为@Service后,会有什么好处?
java·后端·spring
大飞pkz42 分钟前
【C#】正则表达式
开发语言·正则表达式·c#·string·字符串匹配·高效字符串匹配
猪蹄手1 小时前
C/C++基础详解(三)
开发语言·jvm·c++
freed_Day2 小时前
Java进阶学习之不可变集合
java·学习
阿巴~阿巴~2 小时前
string 类元素访问方法
开发语言·c++