Java 8 Stream 流实战指南:从入门到进阶

举报
Brian-Tony 发表于 2026/10/22 08:51:58 2026/10/22
【摘要】 Java 8 引入的 Stream API 是处理集合数据的利器,用好了能让代码简洁优雅,用不好反而让人看不懂。本文带你从零开始掌握 Stream 流的核心用法,涵盖常用操作、实战案例和避坑指南,适合日常开发查阅。 一、什么是 Stream?Stream 是对集合(Collection)的增强,它允许你以声明式的方式处理数据。简单说,以前遍历集合要写 for 循环,现在可以用链式调用一步到位...

Java 8 引入的 Stream API 是处理集合数据的利器,用好了能让代码简洁优雅,用不好反而让人看不懂。本文带你从零开始掌握 Stream 流的核心用法,涵盖常用操作、实战案例和避坑指南,适合日常开发查阅。

一、什么是 Stream?

Stream 是对集合(Collection)的增强,它允许你以声明式的方式处理数据。简单说,以前遍历集合要写 for 循环,现在可以用链式调用一步到位。

Stream 有三个特点:

  • 不存储数据:Stream 只是数据的"管道",数据来自集合、数组或 IO 源
  • 不修改源数据:Stream 操作不会改变原始集合,而是返回新结果
  • 惰性求值:中间操作不会立即执行,只有遇到终端操作才会真正计算

二、Stream 的操作分类

Stream 操作分为两类:

类型 说明 常见方法
中间操作 返回 Stream,可链式调用 filter、map、sorted、distinct、limit、skip
终端操作 返回具体结果,触发执行 collect、forEach、count、reduce、anyMatch、findFirst

一个 Stream 只能被消费一次,终端操作执行后 Stream 就关闭了,再次使用会抛异常。

三、常用操作详解

1. 创建 Stream

import java.util.*;
import java.util.stream.*;

public class StreamCreateDemo {
    public static void main(String[] args) {
        // 从集合创建
        List<String> list = Arrays.asList("Java", "Python", "Go");
        Stream<String> stream1 = list.stream();

        // 从数组创建
        Stream<Integer> stream2 = Stream.of(1, 2, 3, 4, 5);

        // 从范围创建
        IntStream stream3 = IntStream.rangeClosed(1, 10);

        // 无限流
        Stream<Double> stream4 = Stream.generate(Math::random).limit(5);
    }
}

2. filter:过滤

List<Integer> nums = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<Integer> evens = nums.stream()
        .filter(n -> n % 2 == 0)
        .collect(Collectors.toList());
// [2, 4, 6, 8, 10]

3. map:转换

List<String> names = Arrays.asList("java", "python", "go");
List<String> upper = names.stream()
        .map(String::toUpperCase)
        .collect(Collectors.toList());
// ["JAVA", "PYTHON", "GO"]

4. flatMap:扁平化

List<List<Integer>> nested = Arrays.asList(
        Arrays.asList(1, 2),
        Arrays.asList(3, 4),
        Arrays.asList(5, 6)
);
List<Integer> flat = nested.stream()
        .flatMap(List::stream)
        .collect(Collectors.toList());
// [1, 2, 3, 4, 5, 6]

5. sorted:排序

List<Integer> nums = Arrays.asList(5, 3, 8, 1, 9);
List<Integer> sorted = nums.stream()
        .sorted()
        .collect(Collectors.toList());
// [1, 3, 5, 8, 9]

// 自定义排序
List<String> names = Arrays.asList("Java", "python", "Go", "Rust");
List<String> sorted2 = names.stream()
        .sorted(String.CASE_INSENSITIVE_ORDER)
        .collect(Collectors.toList());
// ["Go", "Java", "python", "Rust"]

6. distinct:去重

List<Integer> nums = Arrays.asList(1, 2, 2, 3, 3, 3, 4);
List<Integer> unique = nums.stream()
        .distinct()
        .collect(Collectors.toList());
// [1, 2, 3, 4]

7. limit / skip:截取

List<Integer> nums = IntStream.rangeClosed(1, 10).boxed().collect(Collectors.toList());
List<Integer> result = nums.stream()
        .skip(3)    // 跳过前3个
        .limit(4)   // 取4个
        .collect(Collectors.toList());
// [4, 5, 6, 7]

四、终端操作

1. collect:收集结果

List<String> names = Arrays.asList("Java", "Python", "Go", "Rust");

// 转 List
List<String> list = names.stream().collect(Collectors.toList());

// 转 Set
Set<String> set = names.stream().collect(Collectors.toSet());

// 转 Map
Map<String, Integer> map = names.stream()
        .collect(Collectors.toMap(
                name -> name,
                String::length
        ));
// {Java=4, Python=6, Go=2, Rust=4}

// 分组
Map<Integer, List<String>> grouped = names.stream()
        .collect(Collectors.groupingBy(String::length));
// {2=[Go], 4=[Java, Rust], 6=[Python]}

// 拼接字符串
String joined = names.stream()
        .collect(Collectors.joining(", "));
// "Java, Python, Go, Rust"

2. forEach:遍历

List<String> names = Arrays.asList("Java", "Python", "Go");
names.stream().forEach(System.out::println);

3. reduce:归约

List<Integer> nums = Arrays.asList(1, 2, 3, 4, 5);

// 求和
int sum = nums.stream().reduce(0, Integer::sum);
// 15

// 求最大值
Optional<Integer> max = nums.stream().reduce(Integer::max);
// Optional[5]

// 拼接字符串
String result = nums.stream()
        .map(String::valueOf)
        .reduce("", (a, b) -> a + b);
// "12345"

4. 匹配与查找

List<Integer> nums = Arrays.asList(1, 2, 3, 4, 5);

boolean anyEven = nums.stream().anyMatch(n -> n % 2 == 0);   // true
boolean allPositive = nums.stream().allMatch(n -> n > 0);     // true
boolean noneNegative = nums.stream().noneMatch(n -> n < 0);   // true

Optional<Integer> first = nums.stream().filter(n -> n > 3).findFirst(); // Optional[4]
Optional<Integer> any = nums.stream().filter(n -> n > 3).findAny();     // Optional[4]

五、实战案例

案例 1:从用户列表中提取符合条件的数据

@Data
@AllArgsConstructor
class User {
    private String name;
    private Integer age;
    private String city;
    private BigDecimal salary;
}

List<User> users = Arrays.asList(
        new User("张三", 25, "北京", new BigDecimal("15000")),
        new User("李四", 30, "上海", new BigDecimal("25000")),
        new User("王五", 28, "北京", new BigDecimal("18000")),
        new User("赵六", 35, "杭州", new BigDecimal("30000")),
        new User("钱七", 22, "上海", new BigDecimal("12000"))
);

// 找出北京年龄大于25的用户,按薪资降序,取前2个的名字
List<String> result = users.stream()
        .filter(u -> "北京".equals(u.getCity()))
        .filter(u -> u.getAge() > 25)
        .sorted(Comparator.comparing(User::getSalary).reversed())
        .limit(2)
        .map(User::getName)
        .collect(Collectors.toList());
// ["王五"]

案例 2:分组统计

// 按城市分组,统计平均薪资
Map<String, Double> avgSalaryByCity = users.stream()
        .collect(Collectors.groupingBy(
                User::getCity,
                Collectors.averagingDouble(u -> u.getSalary().doubleValue())
        ));
// {北京=16500.0, 上海=18500.0, 杭州=30000.0}

// 按城市分组,取每组薪资最高的用户
Map<String, Optional<User>> topByCity = users.stream()
        .collect(Collectors.groupingBy(
                User::getCity,
                Collectors.maxBy(Comparator.comparing(User::getSalary))
        ));

案例 3:多级分组

// 先按城市分组,再按年龄是否大于25分组
Map<String, Map<Boolean, List<User>>> grouped = users.stream()
        .collect(Collectors.groupingBy(
                User::getCity,
                Collectors.partitioningBy(u -> u.getAge() > 25)
        ));

六、避坑指南

1. Stream 只能消费一次

Stream<String> stream = list.stream();
stream.forEach(System.out::println);
stream.forEach(System.out::println); // ❌ IllegalStateException

2. 不要在 forEach 中修改外部变量

int sum = 0;
nums.stream().forEach(n -> sum += n); // ❌ 编译错误,sum 必须是 final

应该用 reduce 或 summingInt。

3. parallelStream 不是万能的

list.parallelStream().forEach(System.out::println); // 输出顺序不确定

并行流适合 CPU 密集型的大数据量计算,小数据量反而更慢。而且要注意线程安全问题,不要在并行流中操作共享可变状态。

4. collect(toMap) 键重复会抛异常

// 如果有重复 key,会抛 IllegalStateException
Map<String, Integer> map = users.stream()
        .collect(Collectors.toMap(User::getCity, User::getAge));

// 解决:提供合并函数
Map<String, Integer> map2 = users.stream()
        .collect(Collectors.toMap(
                User::getCity,
                User::getAge,
                (v1, v2) -> v1  // 保留第一个
        ));

七、常用 Collectors 速查

方法 用途
toList() 转 List
toSet() 转 Set
toMap(key, value) 转 Map
joining(delimiter) 拼接字符串
counting() 计数
summingInt/Long/Double 求和
averagingInt/Long/Double 求平均
maxBy/minBy(comparator) 最大/最小值
groupingBy(classifier) 分组
partitioningBy(predicate) 分区(true/false 两组)
reducing(identity, op) 自定义归约

总结

Stream 流让集合操作变得简洁优雅,但也要避免过度使用。简单的遍历用 for 循环反而更清晰,复杂的链式调用要注意可读性,必要时可以拆成多行或提取方法。掌握 Stream 的核心操作和 Collectors,日常开发中 80% 的集合处理场景都能优雅搞定。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。