写在最前面,阅读本文需要对 lambda表达式和 方法引用相对熟悉;
1. Stream流介绍
一种支持顺序和并行聚合操作的元素序列,能够对集合、数组进行过滤、排序、去重等操作;
Stream流与日常IO流是不同的技术体系;
官方定义:
A sequence of elements supporting sequential and parallel aggregate operations.
Stream的两种操作:
public class StreamTest {
public static void main(String[] args) {
Stream<String> names = Stream.of("tom", "jerry", "jack", "lisa", "mark", "bob", "kevin", "jon");
// 中间操作
Stream<String> namesOfStartsWithJ = names.filter(item -> item.startsWith("j"));
// 终止操作
namesOfStartsWithJ.forEach(System.out::println);
}
}
// 运行结果
jerry
jack
jon
2. Stream的创建方式
-
数组
public class StreamTest { public static void main(String[] args) { String[] names = {"tom", "jerry", "jack", "lisa", "mark", "bob", "kevin", "jon"}; // Arrays工具类的静态方法stream() Stream<String> namesOfStream = Arrays.stream(names); } }
-
集合
public class StreamTest { public static void main(String[] args) { String[] names = {"tom", "jerry", "jack", "lisa", "mark", "bob", "kevin", "jon"}; List<String> nameList = Arrays.asList(names); // Collection接口的默认方法stream()和parallelStream() Stream<String> namesOfStream1 = nameList.stream(); // 顺序流 Stream<String> namesOfStream2 = nameList.parallelStream(); // 并行流 } }
-
Stream接口的静态方法
public class StreamTest { public static void main(String[] args) { // Stream接口的静态方法of() Stream<String> namesOfStream1 = Stream.of("tom"); Stream<String> namesOfStream2 = Stream.of("tom", "jerry", "jack", "lisa", "mark", "bob", "kevin", "jon"); // Stream接口的静态方法iterate()和generate()生成的无限流 Stream<Integer> namesOfStream3 = Stream.iterate(1, item -> item + 1); // 迭代方式产生 Stream<Integer> namesOfStream4 = Stream.generate(() -> new Random().nextInt(100)); // 生成方式产生 // Stream接口的静态方法empty()生成的空流 Stream<Integer> namesOfStream5 = Stream.empty(); } }
3. Stream的中间操作
-
过滤
- filter:对元素进行过滤;
- distinct:对元素进行去重,必须重写元素的hashCode()和equals()方法;
public class StreamTest { public static void main(String[] args) { Stream<String> names = Stream.of("tom", "jerry", "jack", "lisa", "mark", "bob", "kevin", "jon", "jack"); // 过滤不是'j'开头的名称 names.filter(item -> item.startsWith("j")) // 去掉重复的名称 .distinct() .forEach(System.out::println); } }
// 运行结果 jerry jack jon
-
切片
- limit:截取前面n个元素;
- skip:跳过前面n个元素,当流中元素个数不足n个时,将返回一个空流;
public class StreamTest { public static void main(String[] args) { Stream<String> names = Stream.of("tom", "jerry", "jack", "lisa", "mark", "bob", "kevin", "jon", "jack"); // 截取前面3个元素 names.limit(3) // 跳过前面2个元素 .skip(2) .forEach(System.out::println); } }
// 运行结果 jack
-
映射
- map:对元素进行操作,转换为另一种类型;
- flatMap:对元素进行操作,转换为一种Stream流;
public class StreamTest { public static void main(String[] args) { Stream<String> names = Stream.of("tom", "jerry", "jack"); // 将string名称转换为User对象 names.map(User::new).forEach(System.out::println); } } class User { private String name; public User(String name) { this.name = name; } @Override public String toString() { return "User: {name=" + name + "}"; } }
// 运行结果 User: {name=tom} User: {name=jerry} User: {name=jack}
public class StreamTest { public static void main(String[] args) { Integer[] arr1 = {1, 2, 3}; Integer[] arr2 = {4, 5, 6}; Integer[] arr3 = {7, 8, 9}; Stream<Integer[]> stream = Stream.of(arr1, arr2, arr3); // 将二维数组流转换为一维数组流 stream.flatMap(Arrays::stream).forEach(System.out::println); } }
// 运行结果 1 2 3 4 5 6 7 8 9
-
排序
- sorted:若元素实现了Comparable接口,重写了compareTo方法,则采用元素内部的方法比较,否则需要传入自定义的比较函数;
public class StreamTest { public static void main(String[] args) { Stream<String> names = Stream.of("tom", "jerry", "jack", "lisa", "mark", "bob", "kevin", "jon", "jack"); // 将名称进行排序 names.sorted().forEach(System.out::println); } }
// 运行结果 bob jack jack jerry jon kevin lisa mark tom
4. Stream的终止操作
-
查找
- findFirst:返回第一个元素,注意不是直接返回的元素对象,而是被Optional容器包装的元素对象;
- findAny:返回任意一个元素,注意也是返回的被Optional容器包装的元素对象;
- max:返回元素中的最大值,需要传入比较函数,注意也是返回的被Optional容器包装的元素对象;
- min:返回元素中的最小值,需要传入比较函数,注意也是返回的被Optional容器包装的元素对象;
public class StreamTest { public static void main(String[] args) { Stream<Integer> stream1 = Stream.of(1, 2, 3, 4, 5, 6); Optional<Integer> first = stream1.findFirst(); System.out.println("first: " + first.get()); Stream<Integer> stream2 = Stream.of(1, 2, 3, 4, 5, 6); Optional<Integer> any = stream2.findAny(); System.out.println("any: " + any.get()); Stream<Integer> stream3 = Stream.of(1, 2, 3, 4, 5, 6); Optional<Integer> max = stream3.max(Integer::compareTo); System.out.println("max: " + max.get()); Stream<Integer> stream4 = Stream.of(1, 2, 3, 4, 5, 6); Optional<Integer> min = stream4.min(Integer::compareTo); System.out.println("min: " + min.get()); } }
// 运行结果 first: 1 any: 1 max: 6 min: 1
-
匹配
- allMatch:是否所有元素都匹配;
- anyMatch:是否有任意一个元素匹配;
- noneMatch:是否所有元素都不匹配;
public class StreamTest { public static void main(String[] args) { Stream<String> stream1 = Stream.of("tom", "jerry", "jack", "lisa", "mark", "bob", "kevin", "jon", "jack"); // 是否所有名称长度都小于6 boolean result1 = stream1.allMatch(item -> item.length() < 6); System.out.println("result1: " + result1); Stream<String> stream2 = Stream.of("tom", "jerry", "jack", "lisa", "mark", "bob", "kevin", "jon", "jack"); // 是否有名称包含字符'z' boolean result2 = stream2.anyMatch(item -> item.contains("z")); System.out.println("result2: " + result2); Stream<String> stream3 = Stream.of("tom", "jerry", "jack", "lisa", "mark", "bob", "kevin", "jon", "jack"); // 是否所有名称都不是'b'开头 boolean result3 = stream3.noneMatch(item -> item.startsWith("b")); System.out.println("result3: " + result3); } }
// 运行结果 result1: true result2: false result3: false
-
统计
- count:统计流中元素个数;
public class StreamTest { public static void main(String[] args) { Stream<String> names = Stream.of("tom", "jerry", "jack", "lisa", "mark", "bob", "kevin", "jon", "jack"); // 统计以'j'开头的名称数量 long size = names.filter(item -> item.startsWith("j")).count(); System.out.println("size: " + size); } }
// 运行结果 size: 4
-
遍历
- forEach:遍历元素
public class StreamTest { public static void main(String[] args) { Stream<String> names = Stream.of("tom", "jerry", "jack", "lisa", "mark", "bob", "kevin", "jon", "jack"); // 遍历以'j'开头的名称 names.filter(item -> item.startsWith("j")).forEach(System.out::println); } }
// 运行结果 jerry jack jon jack
-
聚合
- reduce:按照自定义规则对元素进行聚合操作,并返回期待的结果;
public class StreamTest { public static void main(String[] args) { Stream<Integer> stream1 = Stream.iterate(1, item -> item + 1); // 1~100的和 Optional<Integer> sum1 = stream1.limit(100).reduce(Integer::sum); System.out.println("sum1: " + sum1.get()); Stream<Integer> stream2 = Stream.iterate(1, item -> item + 1); // 初始值为100, 再加上1~100的和 Integer sum2 = stream2.limit(100).reduce(100, Integer::sum); System.out.println("sum2: " + sum2); } }
// 运行结果 sum1: 5050 sum2: 5150
-
收集
- collect:将已操作过的元素收集起来,可返回List、Set、Map等等;
public class StreamTest { public static void main(String[] args) { Stream<Integer> stream1 = Stream.iterate(1, item -> item + 1); // 将1~10的偶数收集到List中 List<Integer> evenNumbers = stream1.limit(10) .filter(item -> item % 2 == 0) .collect(Collectors.toList()); System.out.println("偶数: " + evenNumbers); Stream<Integer> stream2 = Stream.iterate(1, item -> item + 1); // 将1~10的奇偶数分类放入map中 Map<String, List<Integer>> numberMap = stream2.limit(10) .collect( Collectors.toMap( item -> item % 2 == 0 ? "偶数" : "奇数", item -> { List<Integer> list = new ArrayList<>(); list.add(item); return list; }, (oldList, newList) -> { newList.addAll(oldList); return newList; } ) ); System.out.println("奇偶数分类: " + numberMap); } }
// 运行结果 偶数: [2, 4, 6, 8, 10] 奇偶数分类: {偶数=[10, 8, 6, 4, 2], 奇数=[9, 7, 5, 3, 1]}
5. Stream的并行流
- Stream流可以通过实例的parallel和sequential两个方法在并行流和顺序流之间切换;
- Stream并行流是以Java7引入的fork/join框架为基础,以递归方式将可以并行的任务拆分成更小的任务,然后将每个子任务的结果合并起来生成整体结果;
public class StreamTest {
public static void main(String[] args) {
Stream<String> names = Stream.of("tom", "jerry", "jack", "lisa", "mark", "bob");
names.parallel().filter(item -> {
System.out.println("filter: " + "thread=" + Thread.currentThread().getName() + ", item=" + item);
return item.startsWith("j");
}).forEach(item -> System.out.println("result: " + item));
}
}
// 运行结果
filter: thread=main, item=lisa
filter: thread=ForkJoinPool.commonPool-worker-2, item=bob
filter: thread=main, item=tom
filter: thread=ForkJoinPool.commonPool-worker-3, item=jack
filter: thread=ForkJoinPool.commonPool-worker-1, item=jerry
filter: thread=ForkJoinPool.commonPool-worker-2, item=mark
result: jerry
result: jack
6. Stream的注意事项
-
Stream自己不会存储元素;
-
Stream的中间操作不会改变源对象,相反它会返回一个持有结果的新Stream实例;
public class StreamTest { public static void main(String[] args) { Stream<String> stream1 = Stream.of("tom", "jerry", "jack", "lisa", "mark", "bob"); Stream<String> stream2 = stream1.filter(item -> item.startsWith("j")); // stream1与stream2是两个不同的对象 System.out.println(stream1); System.out.println(stream2); } }
// 运行结果 java.util.stream.ReferencePipeline$Head@404b9385 java.util.stream.ReferencePipeline$2@6d311334
-
Stream的中间操作是延迟加载的,只有当终止操作存在的时候,中间操作才会执行;
public class StreamTest { public static void main(String[] args) { Stream<String> stream1 = Stream.of("tom", "jerry", "jack", "lisa", "mark", "bob"); Stream<String> stream2 = stream1.filter(item -> { // 这里的打印不会执行 System.out.println("filter: " + item); return item.startsWith("j"); }); } }
-
Stream的执行实际上是每一个元素沿着执行链垂直移动的,也就是说当一个元素将执行链执行完成后才会开始第二个元素,但注意排序操作例外;
public class StreamTest { public static void main(String[] args) { Stream<Integer> stream1 = Stream.of(3, 1, 2, 5, 6, 4); stream1.sorted((a, b) -> { // 全部元素完成排序 System.out.println("sort: " + a + ", " + b); return Integer.compare(a, b); }).filter(item -> { // 1. 对元素过滤 System.out.println("filter: " + item); return item <= 3; }).map(item -> { // 2. 对元素操作 System.out.println("map: " + item * 2); return item * 2; }).forEach(item -> { // 3. 对元素打印 System.out.println("print: " + item); }); } }
// 运行结果 sort: 1, 3 sort: 2, 1 sort: 2, 3 sort: 2, 1 sort: 5, 2 sort: 5, 3 sort: 6, 3 sort: 6, 5 sort: 4, 3 sort: 4, 6 sort: 4, 5 filter: 1 map: 2 print: 2 filter: 2 map: 4 print: 4 filter: 3 map: 6 print: 6 filter: 4 filter: 5 filter: 6
-
Stream的实例在执行终止操作后不能再执行其他操作,否则会抛出
stream has already been operated upon or closed
异常;