背景
Java8上线很多年了,Stream流基本上是Java8的新特性里面最清爽的一个功能。
有了Stream流,操作集合可以告别繁琐的for循环,简化代码、操作集合可以肆意妄为的获取自己想要的数据、而且操作拥有大量数据的集合的性能也有强大的提升。
学习的路上,做笔记,一起了解一下。
一:Stream 流是如何工作的
流表示包含一系列元素的集合,我们可以对集合进行不同类型的操作,如下
//对象流
List<String> myList = Arrays.asList("a1", "b1", "e1", "d1", "C1", "C1");
myList.stream()
.filter(c -> c.startsWith("C"))//过滤
// .filter(s -> s.startsWith("a"))
.map(String::toLowerCase)//对集合内元素进行操作
.sorted()//排序
.forEach(System.out::println);//遍历对元素进行操作
/*
* c1
* c1
* */
上面的一个简单的流处理。涉及到两个基本的操作:中间操作【filter、map、sort】和终端操作【forEach】
①:中间操作会再次返回一个流,所以,我们可以链接多个中间操作,注意这里是不用加分号的。上图中的 filter 过滤, map 对象转换, sorted 排序,就属于中间操作。
②:终端操作是对流操作的一个结束动作,一般返回 void 或者一个非流的结果。上图中的 forEach循环 就是一个终止操作。
二、不同类型的 Stream 流
我们可以从各种数据源中创建 Stream 流,其中以 Collection 集合最为常见。如 List 和 Set 均支持 stream() 方法来创建顺序流或者是并行流。
并行流是通过多线程的方式来执行的,它能够充分发挥多核 CPU 的优势来提升性能。本文在最后再来介绍并行流,我们先讨论顺序流
像第一部分代码演示的就是一个常规的对象流,除了常规的对象流之外Java 8还附带了一些特殊类型的流,用于处理原始数据类型 int, long以及 double。对应的IntStream, LongStream还有 DoubleStream。
其中, IntStreams.range()方法还可以被用来取代常规的 for 循环, 如下所示:
IntStream.range(1, 4).forEach(System.out::println);
// 相当于 for (int i = 1; i < 4; i++) {}
原始类型流和常规对象流有什么区别呢?
一个部分函数式接口名不一样,功能相同。二个比较好用的在于原始类型流支持额外的终端聚合操作,sum()以及average(),如下所示:
//额外的终端聚合操作, sum()以及 average()
Arrays.stream(new int[]{
1, 2, 3})
.map(n -> 2 * n + 1)//对数值中的每个对象执行 2*n + 1 操作
.average()// 求平均值
.ifPresent(System.out::println);// 如果值不为空,则输出
eg:应用场景
常规对象流和原始类型流互相转换,这个时候,中间操作 mapToInt(),mapToLong()和mapToDouble()和mapToObj()就派上用场了
//常规对象流转换为原始类型流
Stream.of("a1", "b1", "e1", "d1", "C1", "C1")
.map(s -> s.substring(1))
// .map(Integer::parseInt)
.mapToInt(Integer::parseInt)
.max()
.ifPresent(System.out::println);// 如果值不为空,则输出
//常规对象流转换为原始类型流
IntStream.range(1, 4)
.mapToObj(i -> "a" + i)
.forEach(System.out::println);// 相当于 for (int i = 1; i < 4; i++) {}
三、Stream 流的处理顺序
大致了解不同类型的Stream流之后,再了解一下数据流的执行顺序。在此之前需要了解流的中间操作另外一个特性------延迟性。先上代码再做总节
下面是一个没有终端操作的实例代码
Stream.of("a1", "b1", "e1", "d1", "C1", "C1")
.filter(s -> {
System.out.println("filter:" + s);
return true;
});
执行这段代码会有什么结果呢?什么结果也没有! 因为:当且仅当存在终端操作时,中间操作才会被执行。
下面来验证一下
Stream.of("a1", "b1", "e1", "d1", "C1", "C1")
.filter(s -> {
System.out.println("filter:" + s);
return true;
})
.forEach(s -> System.out.println("Foreach:" + s));
执行本次代码,控制台会输出
filter:a1
Foreach:a1
filter:b1
Foreach:b1
filter:e1
Foreach