在 Java 8 中引入的 Stream API,彻底改变了我们处理集合和数组的方式。它结合 Lambda 表达式,提供了一种声明式、链式的操作风格,让数据处理代码更简洁、更易读。本文将从 Stream 的基本概念、获取方式、核心操作到实际应用,全面梳理 Stream 流的使用方法。
一、什么是 Stream 流?
Stream 流是 Java 8 新增的用于处理集合和数组的 API,它具有以下特点:
- 流式处理:数据像水流一样在管道中传递,经过一系列操作后输出结果
- 声明式编程:关注 "做什么" 而不是 "怎么做"
- 链式操作:支持多个操作串联执行
- 惰性执行:中间操作不会立即执行,直到遇到终结操作才会触发实际计算
使用 Stream 流的基本步骤:
- 获得一个 Stream 流(将数据放入流水线)
- 使用中间方法处理数据(对流水线上的数据进行加工)
- 使用终结方法获取结果(从流水线上取出最终数据)
二、如何获取 Stream 流?
根据数据源的不同,Stream 流有多种获取方式:
1. 从单列集合获取
所有实现了 Collection 接口的单列集合(如 List、Set)都可以通过stream()方法直接获取 Stream 流:
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "a", "b", "c", "d");
// 获取Stream流并遍历
list.stream().forEach(s -> System.out.println(s));
2. 从双列集合获取
Map 是双列集合,不能直接获取 Stream 流,需要先转换为单列集合视图:
HashMap<String, Integer> hashMap = new HashMap<>();
hashMap.put("aaa", 111);
hashMap.put("bbb", 222);
// 方式1:获取key的Stream流
hashMap.keySet().stream().forEach(s -> System.out.println(s));
// 方式2:获取entry的Stream流
hashMap.entrySet().stream().forEach(s -> System.out.println(s));
3. 从数组获取
可以通过Arrays.stream()方法从数组获取 Stream 流:
int[] arr = {1, 2, 3, 5, 6, 7};
// 获取数组的Stream流并遍历
Arrays.stream(arr).forEach(s -> System.out.println(s));
4. 从零散数据获取
通过Stream.of()方法可以将零散数据转换为 Stream 流:
// 处理零散数据
Stream.of(1, 2, 3, 4, 5).forEach(s -> System.out.println(s));
// 注意:处理基本类型数组时会将整个数组作为一个元素
int[] nums = {1,2,3};
Stream.of(nums).forEach(System.out::println); // 输出数组地址
三、Stream 流的中间操作
中间操作会返回一个新的 Stream 流,支持链式调用,常用的中间操作包括:
1. 过滤(filter)
根据条件筛选流中的元素,符合条件的元素保留:
List<String> names = new ArrayList<>();
Collections.addAll(names, "韩孟超", "韩小孩", "王不懂", "网易");
// 筛选出以"韩"开头的名字
names.stream()
.filter(s -> s.startsWith("韩"))
.forEach(s -> System.out.println(s)); // 输出:韩孟超、韩小孩
2. 限制与跳过(limit/skip)
limit(n):保留流中的前 n 个元素skip(n):跳过流中的前 n 个元素// 获取前3个元素 names.stream().limit(3).forEach(System.out::println); // 跳过前3个元素,获取剩余元素 names.stream().skip(3).forEach(System.out::println);
3. 去重(distinct)
去除流中的重复元素,依赖元素的hashCode()和equals()方法:
List<String> namesWithDuplicate = new ArrayList<>();
Collections.addAll(namesWithDuplicate, "韩孟超", "韩小孩", "韩小孩");
// 去除重复元素
namesWithDuplicate.stream()
.distinct()
.forEach(System.out::println); // 输出:韩孟超、韩小孩
4. 合并流(concat)
将两个流合并为一个流:
Stream<String> stream1 = names.stream().distinct();
Stream<String> stream2 = names.stream().limit(2);
// 合并两个流
Stream.concat(stream1, stream2).forEach(System.out::println);
5. 转换类型(map)
将流中的元素转换为另一种类型:
List<String> list = new ArrayList<>();
Collections.addAll(list, "韩孟超", "王不懂");
// 将字符串转换为布尔值(是否以"韩"开头)
list.stream()
.map(s -> s.startsWith("韩"))
.forEach(v -> System.out.println(v)); // 输出:true、false
四、Stream 流的终结操作
终结操作会触发流的实际处理,并产生一个结果或副作用,常用的终结操作包括:
1. 遍历(forEach)
对流中的每个元素执行指定操作:
names.stream().forEach(s -> System.out.println(s));
2. 统计元素数量(count)
返回流中元素的个数:
long count = names.stream().count();
System.out.println("元素数量:" + count);
3. 收集到数组(toArray)
将流中的元素收集到数组中:
String[] array = names.stream()
.toArray(value -> new String[value]);
System.out.println(Arrays.toString(array));
4. 收集到集合(collect)
使用Collectors工具类将流中的元素收集到 List、Set、Map 等集合中:
// 收集到List
List<String> listResult = names.stream()
.filter(s -> s.startsWith("韩"))
.collect(Collectors.toList());
// 收集到Set
Set<String> setResult = names.stream()
.filter(s -> s.length() > 2)
.collect(Collectors.toSet());
// 收集到Map(需要指定key和value的生成规则)
List<String> userInfo = new ArrayList<>();
Collections.addAll(userInfo, "张三-21-男", "李四-22-男");
Map<String, Integer> mapResult = userInfo.stream()
.collect(Collectors.toMap(
s -> s.split("-")[0], // key:姓名
s -> Integer.parseInt(s.split("-")[1]) // value:年龄
));
五、Stream 流的使用注意事项
- 流只能使用一次:一个 Stream 流对象只能执行一次终结操作,再次使用会抛出异常
- 不会修改源数据:Stream 流的操作不会改变原始集合或数组中的数据
- 惰性执行:中间操作只有在遇到终结操作时才会执行
- 并行流:对于大数据量,可以使用
parallelStream()获取并行流提高处理效率
六、总结
Stream 流为 Java 集合和数组的处理提供了一种更优雅、更高效的方式。通过链式调用的中间操作和灵活的终结操作,我们可以用更少的代码实现复杂的数据处理逻辑。掌握 Stream 流的使用,不仅能提高开发效率,还能让代码更具可读性和可维护性。
无论是简单的过滤排序,还是复杂的数据转换和聚合,Stream 流都能胜任。在实际开发中,应充分利用 Stream 流的特性,写出更简洁、更现代的 Java 代码。
654

被折叠的 条评论
为什么被折叠?



