一、关于Stream
Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。
特性:
- 无存储。stream不是一种数据结构,它只是某种数据源的一个视图,数据源可以是一个数组,Java容器或I/O channel等。
- 为函数式编程而生。对stream的任何修改都不会修改背后的数据源,比如对stream执行过滤操作并不会删除被过滤的元素,而是会产生一个不包含被过滤元素的新stream。
- 惰式执行。stream上的操作并不会立即执行,只有等到用户真正需要结果的时候才会执行。
- 可消费性。stream只能被“消费”一次,一旦遍历过就会失效,就像容器的迭代器那样,想要再次遍历必须重新生成。
可以抽象如下:
数据源(source) -> 数据处理/转换(intermedia) -> 结果处理(terminal )
二、使用
2.1 生成Stream
- 使用Collection下的 stream() 和 parallelStream() 方法
- 使用Arrays 中的 stream() 方法,将数组转成流
- 使用Stream中的静态方法:of()、iterate()、generate()
- 使用 BufferedReader.lines() 方法,将每行内容转成流
- 使用 Pattern.splitAsStream() 方法,将字符串分隔成流
2.2 操作Stream
对stream的操作分为为两类,中间操作(intermediate operations)和结束操作(terminal operations),二者特点是:
- 中间操作总是会惰式执行,调用中间操作只会生成一个标记了该操作的新stream
- 结束操作会触发实际计算,计算发生时会把所有中间操作积攒的操作以pipeline的方式执行,这样可以减少迭代次数,计算完成之后stream就会失效
中间操作又可以分为无状态的和有状态的:无状态中间操作是指元素的处理不受前面元素的影响,而有状态的中间操作必须等到所有元素处理之后才知道最终结果,比如排序是有状态操作,在读取所有元素之前并不能确定排序结果
结束操作又可以分为短路操作和非短路操作:短路操作是指不用处理全部元素就可以返回结果,比如找到第一个满足条件的元素。之所以要进行如此精细的划分,是因为底层对每一种情况的处理方式不同。
中间操作(Intermediate operations):
- 无状态(Stateless):unordered() filter() map() mapToInt() mapToLong() mapToDouble() flatMap() flatMapToInt() flatMapToLong() flatMapToDouble() peek()
- 有状态(Stateful):distinct() sorted() limit() skip()
结束操作(Terminal operations):
- 非短路操作:forEach() forEachOrdered() toArray() reduce() collect() max() min() count()
- 短路操作(short-circuiting):anyMatch() allMatch() noneMatch() findFirst() findAny()
2.3 实例
/**
* 创建
* @throws FileNotFoundException
*/
public static void source() throws FileNotFoundException {
// 使用Collection
List<String> list = Arrays.asList("Messi", "Neymar", "Suárez");
Stream<String> collectionStream = list.stream();
Stream<String> collectionParallelStream = list.parallelStream();
// 使用Arrays
String[] array = new String[]{"Benzema", "CR7", "Bale"};
Stream<String> arrayStream = Arrays.stream(array);
// 使用Stream
Stream<String> ofStream = Stream.of("Salah", "Mane", "Firmino");
Stream<UUID> generateStream = Stream.generate(UUID::randomUUID);
Stream<Integer> integerStream = Stream.iterate(1, integer -> integer + 3);
IntStream rangeStream = IntStream.range(1, 10);
// 使用BufferedReader
BufferedReader bufferedReader = new BufferedReader(new FileReader("README.md"));
// 使用Pattern
Pattern pattern = Pattern.compile("A");
Stream<String> patternStream = pattern.splitAsStream("ABCDE");
}
/**
* 中间操作
*/
public static void intermediate() {
List<String> list1 = Arrays.asList("Messi", "Neymar", "Suárez");
List<String> list2 = Arrays.asList("Benzema", "CR7", "Bale");
List<String> list3 = Arrays.asList("Salah", "Mane", "Firmino", "Mane");
// 使用filter过滤
System.out.println(list1.stream().filter(s -> s.length() > 4).count());
// 使用map映射
System.out.println(list1.stream().map(s -> s.length()).collect(Collectors.toList()));
// 使用flatMap合并
System.out.println(Stream.of(list1, list2, list3).flatMap(list -> list.stream()).collect(Collectors.toList()));
// 使用peek方法,主要用于调试,以便在元素流过管道中的某个点时查看它们,输出集合所有元素
list1.stream().peek(s -> System.out.println(s)).count();
// 使用distinct去重
System.out.println(list3.stream().distinct().collect(Collectors.toList()));
// 使用sort排序
System.out.println(list3.stream().sorted(Comparator.comparing(s -> s.length())).collect(Collectors.toList()));
// 使用limit获取指定数量元素
System.out.println(list1.stream().limit(2).collect(Collectors.toList()));
// 使用skip跳过指定数量元素
System.out.println(list1.stream().skip(2).collect(Collectors.toList()));
}
/**
* 结果处理
*/
public static void terminal() {
List<String> list1 = Arrays.asList("Messi", "Neymar", "Suárez");
// forEach遍历
list1.stream().forEach(System.out::println);
// forEachOrdered遍历,相较于forEach,该方法可以保证在并行流中消费的顺序
list1.parallelStream().forEachOrdered(System.out::println);
// 使用toArray转化为数组
System.out.println(list1.stream().toArray());
// 使用collect收集转换数据
// 转化为map
System.out.println(list1.stream().collect(Collectors.toMap(Function.identity(), s -> s.length())));
// 转化为逗号分隔的字符串
String str = list1.stream().collect(Collectors.joining(","));
System.out.println(str);
// 分组聚合
System.out.println(list1.stream().collect(Collectors.groupingBy(s -> s.length())));
// 按条件分为两组
System.out.println(list1.stream().collect(Collectors.partitioningBy(s -> s.length() > 4)));
// 使用reduce,找到length最小的元素
System.out.println(list1.stream().reduce((s1, s2) -> s1.length() > s2.length() ? s2 : s1));
// 使用max找到最大元素,min类似
System.out.println(list1.stream().max(Comparator.comparing(Function.identity())));
// 使用count计数
System.out.println(list1.stream().count());
// 使用findFirst获取第一个元素
System.out.println(list1.stream().findFirst());
// 使用findAny获取任一元素
System.out.println(list1.stream().filter(s -> s.length() > 1).findAny());
// 使用anyMatch,有一个符合条件就返回true,allMatch、noneMatch类似
System.out.println(list1.stream().anyMatch(s -> s.length() > 5));
}