Java Stream流基本使用
1. Stream简介
Stream是Java8中对集合数据操作的一种新的抽象。Stream不是集合元素本身,它只是对数据源(集合、数组等)进行的操作(过滤、映射、排序等)。这些操作会产生另外一个Stream作进一步操作,最后生成一个结果。Stream操作是非破坏性的,它会总产生新的数据集。
2. 获取Stream
可以从Collection、数组或I/O资源中获取Stream:
- collection.stream():获取Collection流
- Arrays.stream(array):获取数组流
- BufferedReader.lines():获取文本流 例如:
List<Integer> list = Arrays.asList(1, 2, 3);
Stream<Integer> stream = list.stream();
int[] arr = new int[]{1, 2, 3};
IntStream intStream = Arrays.stream(arr);
3. 中间操作
中间操作返回一个新的Stream,可以有多个。它们只是一种标记,只有在终止操作时才会真正执行。常用的中间操作有:
- filter:过滤元素
- map:映射元素
- flatMap:将流中的每个元素替换为另一个流,然后合并这些流
- distinct:去除重复元素
- sorted:排序流中的元素
- limit:截取流中的元素
- skip:跳过流中的前N个元素 等等。
4. 终止操作
终止操作会产生结果或副作用,之后Stream管道被关闭。常用的终止操作有:
- forEach:遍历每个元素
- count:计算流中元素的总数
- collect:收集流中的元素到一个Collection中
- reduce:将流中的元素组合起来
- min/max:获取流中元素的最小值/最大值
- anyMatch:检查是否至少匹配一个元素
- allMatch:检查是否匹配所有元素
- noneMatch:检查是否没有匹配的元素
- findFirst:返回第一个元素
- findAny:返回当前流中的任意元素 等等。
5. 流的消耗
在连续调用中间操作和终止操作之后,Stream会被消耗,不能再被操作。 有几种情况会导致Stream被消耗:
- 调用一个终止操作
- 重复调用同一个Stream上的终止操作
- 在中间操作过程中抛出异常 所以我们要避免重复消耗Stream,导致程序出错。
6. 无限流
可以使用静态方法Stream.iterate()
和Stream.generate()
创建无限流。使用时需要手动限制流大小,否则会导致OutOfMemoryError。
例如:
Stream.iterate(0, n -> n + 2).limit(10).forEach(System.out::println);
Stream.generate(Math::random).limit(5).forEach(System.out::println);
7. 数字流
使用IntStream、LongStream和DoubleStream可以专门操作基本数值类型的流。它们分别以int、long和double作为操作元素。 可以从boxed数值Stream获取,也可以直接从原始类型数组获取:
IntStream.of(arr).min();
IntStream intStream = Arrays.stream(arr);
8. 再次获取Stream
如果想再次操作同一个流,可以使用Iterator
接口中的一个非遍历方法来再次获取,例如:
- iterator().stream()
- spliterator().stream() 例如:
Stream<String> stream = list.stream();
stream.filter(s -> s.length() > 5); //中间操作
Stream<String> stream2 = stream.iterator().stream(); //再次获取流
stream2.forEach(System.out::println); //终止操作
9. 串联Stream
可以使用flatMap()
方法将一个流中的每个值都换成另一个流,然后把所有流连接起来成为一个流:
Stream<List<Integer>> listStream = Stream.of(
Arrays.asList(1, 2),
Arrays.asList(3, 4)
);
Stream<Integer> intStream = listStream.flatMap(list -> list.stream());
//intStream流中包含的数据为[1, 2, 3, 4]。
10. 并行Stream
可以调用parallelStream()
方法将一个顺序流转为并行流,以利用多核CPU的优势加快处理速度。例如:
list.parallelStream().forEach(System.out::println);
11. 串行化Stream
可以调用sequential()
方法将一个并行流转为顺序流。例如:
list.parallelStream().sequential().forEach(System.out::println);
12. Optional
Stream中的某些方法会返回Optional<T>类型的结果,它代表了可能存在或不存在的值。可以使用isPresent()
检查值是否存在,并使用get()
获取值,或使用orElseGet()
给出默认值。
例如:
Optional<Integer> opt = list.stream().findAny();
if(opt.isPresent()) {
Integer n = opt.get();
System.out.println(n);
} else {
Integer n = opt.orElseGet(() -> -1);
System.out.println(n);
}
13. 收集器
Stream提供了Collectors工具类来实现最常见的收集任务。它包含了常见的收集器,可以用于流的collect操作,将流元素收集到一个新的集合中。 常用的收集器有:
- toList:收集到List
- toSet:收集到Set
- toMap:收集到Map
- joining:连接成字符串
- summingInt/averagingInt:计算整数总和/平均值
- reducing:合并流中的元素到一个值 等等。
14. 可选元素
Stream可以处理可选元素,使用Optional类封装 Potential Null值。 这可以避免NullPointerException产生。 Stream上的 Optional相关方法主要有:
- findFirst:返回第一个元素的Optional
- max/min:返回最大/最小元素的Optional
- reduce:可以组合两个 Optional 等等。
Stream API可以有效简化集合数据的操作,让代码变得更加清晰和简洁。理解Stream的各个概念和方法可以让我们运用自如,解决许多日常开发中的数据处理任务。 希望这篇文章能帮助大家基本入门Stream的用法。