一、概述
JDK 1.8新增的Stream,配合Lambda,给操作集合(Collection)提供了极大的便利。
那么什么是Stream?Stream将要处理的元素集合看作一种流,在流的过程中,借助Stream API对流中的元素进行操作,比如:筛选、排序、聚合等。
Stream可以由数组或集合创建,对流的操作分为两种:
-
中间操作,每次返回一个新的流,可以有多个。
-
终端操作,每个流只能进行一次终端操作,终端操作结束后流无法再次使用。终端操作会产生一个新的集合或值。
另外,Stream有几个特性:
-
stream不存储数据,而是按照特定的规则对数据进行计算,一般会输出结果。
-
stream不会改变数据源,通常情况下会产生一个新的集合或一个值。
-
stream具有延迟执行特性,只有调用终端操作时,中间操作才会执行。
二、常用方法
2.1 创建
Stream有三种创建方式
-
通过java.util.Collection.stream()方法用集合创建流
-
使用java.util.Arrays.stream(T[] array)方法用数组创建流
-
使用Stream的静态方法:of()、iterate()、generate()
2.1.1 通过集合创建流
List<String> list = Arrays.asList("a", "b", "c");
// 创建一个顺序流
Stream<String> stream = list.stream();
// 创建一个并行流
Stream<String> parallelStream = list.parallelStream();
-
串行流:适合存在线程安全问题、阻塞任务、重量级任务,以及需要使用同一事务的逻辑。
-
并行流:适合没有线程安全问题、较单纯的数据处理任务。
2.1.2 使用数组创建流
int[] array = {1,3,5,6,8};
IntStreamstream= Arrays.stream(array);
2.1.3 使用静态方法
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);
Stream<Integer> stream2 = Stream.iterate(0, (x) -> x + 3).limit(4);
stream2.forEach(System.out::println);
Stream<Double> stream3 = Stream.generate(Math::random).limit(3);
stream3.forEach(System.out::println);
输出结果:
0 3 6 9
0.6796156909271994
0.1914314208854283
0.8116932592396652
stream和parallelStream的简单区分:stream是顺序流,由主线程按顺序对流执行操作,而parallelStream是并行流,内部以多线程并行执行的方式对流进行操作,但前提是流中的数据处理没有顺序要求。例如:筛选集合中的奇数,两者的处理不同之处:如果流中的数据量足够大,并行流可以加快处速度。
除了直接创建并行流,还可以通过parallel()把顺序流转换成并行流:
Optional<Integer> findFirst = list.stream().parallel().filter(x -> x > 6).findFirst();
关于更多的Parallel Stream,请参考:Java Parallel Stream和Java Stream之Parallel Streams编程指南
2.2 中间操作
2.2.1 筛选与切片
-
filter(Predicate):筛选流中某些元素
-
limit(long val):截断流,取流中前val个元素
-
skip(n):跳过n元素,配合limit(n)可实现分页
-
distinct():通过流所生成元素的equals和hashCode去重
2.2.2 映射
-
map(Function f):接收流中元素,并且将其映射成为新元素,例如:从student对象中取name属性
-
flatMap(Function f):将所有流中的元素并到一起连接成一个流
2.2.3 消费
-
peek(Consumer c):获取流中元素,操作流中元素,与foreach不同的是不会截断流,可继续操作。
使用场景:当遍历完数组后还有后续操作时或list数组转stream时,不适合在用Iterable的foreach循环,这个时候peek就派上用场了。
2.2.4 排序
-
sorted()/sorted(Comparator):产生一个新流,按照自然顺序/比较器规则排序
ArrayList<Integer> number = new ArrayList<>();
number.add(12);
number.add(121);
number.add(23);
number.add(45);
number.add(67);
number.add(67);
number.add(77);
number.add(98);
number.add(99);
number.add(67);
Stream<Integer> distinct = number.stream().filter(x -> x > 50)//121,67,67,77,98,99,67
.skip(2)//67,77,98,99,67
.sorted()//67,67,77,98,99
.distinct()//67,77,98,99
.limit(3);//67,77,98
distinct.forEach(System.out::println);
2.3 终止操作
2.3.1 匹配/聚合操作
匹配
-
allMatch(Predicate):当流中每个元素都符合该断言时才返回true,否则返回false
-
noneMatch(Predicate):当流中每个元素都不符合该断言时才返回true,否则返回false
-
anyMatch(Predicate):只要流中有一个元素满足该断言则返回true,否则返回false