Java 中如何使用 Stream API 来操作集合?
Stream API 是 Java 8 引入的一种新的操作集合的方法。它能让我们以一种更流畅的方式处理一系列的对象。
一、Stream 如何创建
Stream 的创建大致分为两类:
1. 通过 stream() 方法从集合中创建一个 Stream
List<String> list = Arrays.asList("apple", "banana", "cat");
Stream<String> stream = list.stream(); // 从集合中创建 Stream
2. 通过 of() 方法创建一个 Stream
Stream<String> stream = Stream.of("apple", "banana", "cat"); // 创建 Stream
二、Stream 的中间操作
Stream 的中间操作是指对集合中的元素进行筛选、排序、过滤等操作,这些操作仅进行转换,不会影响原始集合。如下是常用的中间操作:
1. filter():筛选符合条件的元素
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> newList = list.stream()
.filter(n -> n % 2 == 0) // 筛选出偶数
.collect(Collectors.toList()); // 转换为 List
System.out.println(newList); // [2, 4]
2. map():将集合中的元素映射为新的元素
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> newList = list.stream()
.map(n -> n * n) // 平方
.collect(Collectors.toList()); // 转换为 List
System.out.println(newList); // [1, 4, 9, 16, 25]
3. sorted():对集合进行排序
List<Integer> list = Arrays.asList(3, 1, 4, 2, 5);
List<Integer> newList = list.stream()
.sorted()
.collect(Collectors.toList()); // 转换为 List
System.out.println(newList); // [1, 2, 3, 4, 5]
4. distinct():对集合进行去重
List<Integer> list = Arrays.asList(3, 1, 4, 2, 5, 1, 2, 4);
List<Integer> newList = list.stream()
.distinct()
.collect(Collectors.toList()); // 转换为 List
System.out.println(newList); // [3, 1, 4, 2, 5]
5. limit():限制集合中输出元素的个数
List<Integer> list = Arrays.asList(3, 1, 4, 2, 5);
List<Integer> newList = list.stream()
.limit(3) // 取前三个
.collect(Collectors.toList()); // 转换为 List
System.out.println(newList); // [3, 1, 4]
6. skip():跳过前几个元素输出
List<Integer> list = Arrays.asList(3, 1, 4, 2, 5);
List<Integer> newList = list.stream()
.skip(3) // 跳过前三个
.collect(Collectors.toList()); // 转换为 List
System.out.println(newList); // [2, 5]
三、Stream 的终止操作
终止操作会触发 Stream 的计算并生成一个结果或者副作用,同时该操作也会关闭 Stream。
1. forEach():遍历 Stream 元素
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5); list.stream().forEach(System.out::println); // 遍历并输出每个元素
2. collect():将 Stream 转换为集合
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5); List<Integer> newList = list.stream().collect(Collectors.toList()); // 转换为 List System.out.println(newList); // [1, 2, 3, 4, 5]
3. count():返回 Stream 元素的个数
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5); long count = list.stream().count(); // 返回元素个数 System.out.println(count); // 5
4. reduce():可以将集合中的元素进行运算,最终返回一个结果
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5); Optional<Integer> optional = list.stream().reduce((a, b) -> a + b); // 求和 System.out.println(optional.get()); // 15
5. min() 和 max():返回集合中的最小值和最大值
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5); Optional<Integer> optional1 = list.stream().min((a, b) -> a.compareTo(b)); // 最小值 Optional<Integer> optional2 = list.stream().max((a, b) -> a.compareTo(b)); // 最大值 System.out.println(optional1.get()); // 1 System.out.println(optional2.get()); // 5
四、Stream 的处理顺序
在 Stream API 中,中间操作和终止操作的顺序会影响最终的结果。中间操作在添加时,只是向 Stream 中添加了一些指令,并不会立即执行。只有在触发终止操作时,这些指令才会一并执行。
优化 Stream 的处理顺序,可以有效提高程序的执行效率。
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Integer result = list.stream()
.filter(n -> {System.out.println("filter:" + n); return n % 2 == 0;})
.map(n -> {System.out.println("map:" + n); return n * 2;})
.findFirst()
.get();
System.out.println(result);
以上代码会输出:
filter:1 filter:2 map:2 4
可以发现,只有在终止操作 findFirst() 被执行时,才会触发中间操作的执行。
五、Stream 的并行处理
在处理大量数据时,使用并行处理可以大大缩短程序的执行时间。
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 1000000; i++) {
list.add(i);
}
long start = System.currentTimeMillis();
long count = list.stream().filter(n -> n % 2 == 0).count();
long end = System.currentTimeMillis();
System.out.println(count);
System.out.println(end - start + "ms"); // 465ms
以上代码使用串行处理 stream(),执行时间大概在 400ms 左右。
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 1000000; i++) {
list.add(i);
}
long start = System.currentTimeMillis();
long count = list.parallelStream().filter(n -> n % 2 == 0).count();
long end = System.currentTimeMillis();
System.out.println(count);
System.out.println(end - start + "ms"); // 143ms
以上代码使用并行处理 parallelStream(),执行时间大概在 100ms 左右,性能提升了 4 倍。
六、Stream 的应用场景
Stream API 可以在很多场景下使用,它可以用来处理集合、文件、网络数据等各种数据源。
1. 对集合进行筛选、排序、过滤等操作
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> newList = list.stream()
.filter(n -> n % 2 == 0) // 筛选出偶数
.sorted() // 排序
.map(n -> n * n) // 平方
.collect(Collectors.toList()); // 转换为 List
System.out.println(newList); // [4, 16]
2. 对文件中的数据进行处理
`java
try (Stream<String> stream = Files.lines(Paths.get("data.txt"))) {
List<String> newList = stream
.filter(line -> !line.startsWith("#")) //
