Java——Stream流
2026/4/27 2:31:38 网站建设 项目流程

平时处理集合、数组,是不是都这么写?定义一个 List,然后写个 for 循环遍历,加 if 判断,过滤数据,再把符合条件的装进新集合,最后再循环输出,这样写十分复杂

自从 Java 8 出了 Stream 流之后,这一切都变了。以前五六行、十几行才能做完的事,现在一行代码搞定。过滤、去重、排序、映射、统计、分组、收集…… 所有你能想到的集合操作,Stream 流几乎都能一条龙解决。


一、Stream定义

Stream 不是集合,不是数据结构,它不存数据,它只是一个 “加工工具”

你可以把它理解成:工厂流水线

  • 集合 / 数组 → 原材料
  • Stream → 流水线
  • 中间操作(过滤、去重、排序) → 加工步骤
  • 终结操作(输出、收集、统计) → 成品出厂

它的工作流程特别简单:

  1. 把集合 / 数组变成一条 “流”
  2. 在流上做各种操作(想怎么加工就怎么加工)
  3. 最后拿到你想要的结果

而且 Stream 有一个很重要的特点:用完就丢,只能用一次。就像矿泉水,你打开喝了就没了,不能倒回去再喝一遍。

再记住一句话:Stream 只负责处理数据,不改变原数据你原来的集合是什么样,处理完还是什么样,不会被修改,这一点特别安全。


二、Stream的优点

1. 代码简洁

以前遍历过滤一个集合:

List<String> list = new ArrayList<>(); for (String s : oldList) { if (s != null && s.length() > 3) { list.add(s); } }

用 Stream:

List<String> list = oldList.stream() .filter(s -> s != null && s.length() > 3) .collect(Collectors.toList());

2. 支持链式编程

list.stream() .filter(...) .map(...) .distinct() .sorted() .collect(...);

从上到下,一步一步, 代码一目了然。


三、Stream 最核心的两个概念

用 Stream 之前,需要记住两个词:中间操作终结操作

1. 中间操作(加工)

  • 比如:过滤 filter、映射 map、去重 distinct、限制 limit、跳过 skip、排序 sorted
  • 特点:可以连着写多个,不会真正执行
  • 就像你在流水线设置好加工步骤,机器还没开动

2. 终结操作(出厂)

  • 比如:遍历 forEach、收集 collect、计数 count、判断 anyMatch、转数组 toArray
  • 特点:只要一执行,整个流才开始跑
  • 执行完,流就关闭了,不能再用

一句话总结:只有调用终结操作,前面的中间操作才会执行;不调用终结操作,等于啥也没干。


四、第一步:怎么获取 Stream 流?(3 种最常用方式)

想使用 Stream,第一步必须先把数据变成 “流”。常用的就 3 种,记这 3 种足够用。

1. 集合 Collection 直接获取(最常用)

List、Set 都能用:

List<String> list = new ArrayList<>(); Stream<String> stream = list.stream(); Stream<String> parallelStream = list.parallelStream();

2. 数组用 Arrays.stream ()

String[] arr = {"张三","李四","王五"}; Stream<String> stream = Arrays.stream(arr);

3. 零散数据用 Stream.of ()

Stream<String> stream = Stream.of("张三","李四","王五","赵六");

五、Stream 常用中间操作:

1. filter:过滤

作用:只留下符合条件的数据参数:一个返回 boolean 的条件,true 留下,false 扔掉

例子:过滤出长度大于 3 的字符串

List<String> list = Stream.of("aa","bbb","cccc","ddddd") .filter(s -> s.length() > 3) .collect(Collectors.toList()); // 结果:cccc、ddddd

2. map:转换

作用:把一种数据,转成另一种数据比如:String 转 Integer、对象转名字、对象转 ID、数字转平方

例子 1:字符串转长度

List<Integer> lengths = Stream.of("张三","张三丰","孙悟空") .map(s -> s.length()) .collect(Collectors.toList()); // 结果:2、3、3

例子 2:用户对象转用户名

List<String> names = userList.stream() .map(user -> user.getName()) .collect(Collectors.toList());

3. distinct:去重

作用:自动去掉重复数据底层用的 equals () 判断,简单粗暴。

List<Integer> list = Stream.of(1,2,2,3,3,3) .distinct() .collect(Collectors.toList()); // 结果:1、2、3

4. sorted:排序

作用:给数据排序两种用法:

  • 无参:自然排序(数字从小到大、字符串按字典序)
  • 有参:自定义排序

例子 1:自然排序

List<Integer> list = Stream.of(3,1,5,2,4) .sorted() .collect(Collectors.toList()); // 1、2、3、4、5

例子 2:倒序

.sorted((a,b) -> b - a)

例子 3:按对象字段排序

.sorted(Comparator.comparing(User::getAge))

5. limit:限制取前几个

作用:只取前 N 个

List<Integer> list = Stream.of(1,2,3,4,5) .limit(3) .collect(Collectors.toList()); // 1、2、3

6. skip:跳过前几个

作用:跳过前 N 个,取后面的

List<Integer> list = Stream.of(1,2,3,4,5) .skip(2) .collect(Collectors.toList()); // 3、4、5

六、Stream 常用终结操作:执行才是硬道理

中间操作设置完,必须调用终结操作,否则代码不执行

1. forEach:遍历

作用:一个一个拿出来处理

Stream.of("张三","李四").forEach(s -> System.out.println(s));

2. collect:收集

作用:把流转成集合、字符串、数组这是最强大的终结操作,必须会。

常用收集方式:

  • 转 List:Collectors.toList()
  • 转 Set:Collectors.toSet()
  • 转 Map:Collectors.toMap(键,值)
  • 拼接字符串:Collectors.joining(",")
  • 分组:Collectors.groupingBy(字段)
  • 统计:Collectors.counting()Collectors.summingInt()

3. count:计数

作用:统计流里有多少个元素

long count = Stream.of(1,2,3).count(); // 3

4. anyMatch:任意一个匹配

作用:只要有一个符合条件,就返回 true

boolean has = Stream.of(1,2,3).anyMatch(s -> s > 2); // true

5. allMatch:全部匹配

作用:所有元素都符合条件才返回 true

6. noneMatch:全都不匹配

作用:一个都不符合才返回 true

7. findFirst:找第一个

作用:返回第一个元素

Optional<String> first = Stream.of("a","b").findFirst();

8. toArray:转数组

作用:流转数组

String[] arr = Stream.of("a","b").toArray(String[]::new);

七、Collectors 工具类:Stream 最强辅助

1. 转 List / Set

collect(Collectors.toList()); collect(Collectors.toSet());

2. 转 Map

注意:键不能重复,否则报错。

Map<Integer, User> map = userList.stream() .collect(Collectors.toMap(User::getId, user -> user));

3. 字符串拼接

把所有元素用符号连起来,超级好用。

String str = Stream.of("张三","李四","王五") .collect(Collectors.joining(",")); // 张三,李四,王五

4. 分组

按某个字段分组,比如按年龄、性别、部门。

Map<Integer, List<User>> map = userList.stream() .collect(Collectors.groupingBy(User::getAge));

结果:key 是年龄,value 是这个年龄的所有人。

5. 统计:最大值、最小值、平均值、总和

// 总和 int sum = userList.stream().collect(Collectors.summingInt(User::getAge)); // 最大值 OptionalInt max = userList.stream().mapToInt(User::getAge).max(); // 平均值 double avg = userList.stream().mapToInt(User::getAge).average().orElse(0);

八、真实案例:

案例 1:过滤 + 转换 + 去重 + 排序 + 收集

需求:从用户列表中,过滤出年龄大于 18 岁的,提取名字,去重,按字典序排序,最后转成 List。

List<String> result = userList.stream() .filter(user -> user.getAge() > 18) .map(User::getName) .distinct() .sorted() .collect(Collectors.toList());

案例 2:根据条件判断是否存在数据

boolean has = userList.stream() .anyMatch(user -> "张三".equals(user.getName()));

案例 3:把 List 转成用逗号分隔的字符串

String str = list.stream().collect(Collectors.joining(","));

案例 4:根据性别分组

Map<String, List<User>> group = userList.stream() .collect(Collectors.groupingBy(User::getSex));

九、Stream 和 普通 for 循环怎么选?

  1. 简单遍历、简单逻辑→ for 循环和 Stream 都行
  2. 过滤、转换、去重、排序、统计、分组→ 优先 Stream,代码简洁
  3. 极度追求性能、超小数据量→ for 循环稍微快一丢丢,但几乎可以忽略
  4. 复杂嵌套逻辑、多跳出条件→ for 循环更清晰

简单说:能使用 Stream 的场景,尽量用 Stream,代码好看、好维护、好改。


十二、总结:

  1. Stream 是数据加工工具,不存数据,不修改原数据
  2. 流只能用一次,用完即关闭
  3. 中间操作不执行,只有终结操作才触发执行
  4. filter 过滤、map 转换、distinct 去重、sorted 排序
  5. collect 收集是最强大的终结操作
  6. Collectors 可以转 List、Set、Map、分组、拼接、统计
  7. 并行流适合大数据量处理
  8. 不要在 Stream 里修改外部变量
  9. toMap 注意键重复问题
  10. 能用 Stream 就用 Stream,代码简洁易维护

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询