一、概念
stream流操作是Java 8提供一个重要新特性,它允许开发人员以声明性方式处理集合,其核心类库主要改进了对集合类的 API和新增Stream操作。Stream类中每一个方法都对应集合上的一种操作。将真正的函数式编程引入到Java中,能让代码更加简洁,极大地简化了集合的处理操作,提高了开发的效率和生产力。
同时stream不是一种数据结构,它只是某种数据源的一个视图,数据源可以是一个数组,Java容器或I/O,channel等。在Stream中的操作每一次都会产生新的流,内部不会像普通集合操作一样立刻获取值,而是惰性取值 ,只有等到用户真正需要结果的时候才会执行。 并且对于现在调用的方法,本身都是一种高层次构件,与线程模型无关。因此在并行使用中,开发者们无需再去操心线程和锁了。Stream内部都已经做好了 。
关于对Stream流的理解,你可以把他当成工厂中的流水线,每个stream流的操作过程遵循着创建 -->操作 -->获取结果的过程,就像流水线上的节点一样组成一个个链条。除此之外你还可以把他理解成sql的视图,集合就相当于数据表中的数据,获取stream流的过程就是确定数据表的属性和元数据的过程,元数据的每一个元素就是表中的数据,对stream流进行操作的过程就是通过sql对这些数据进行查找、过滤、组合、计算、操作、分组等过程,获取结果就是sql执行完毕之后获取的结果视图一样,深入理解stream流可以让我们使用更加简洁的代码获取自己想要的数据。
二、创建流的方式
有几种方法可以创建流。它们可以从集合,数组,文件等创建。
2.1 从集合中创建流
在Java 8中,除了增加了很多Stream相关的类以外,还对集合类自身做了增强,在其中增加了stream方法,可以将一个集合类转换成流。
List<String> list= Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();
以上,通过一个已有的List创建一个流。
除此以外,还有一个parallelStream方法,可以为集合创建一个并行流。(多线程方式,需要考虑线程安全问题)
List<String> list= Arrays.asList("a", "b", "c");
Stream<String> stream = list.parallelStream();
stream.forEach(item->{
System.out.println(item);
});
可以看到,执行结果并不是a,b,c,是因为parallelStream会创建一个并行流,通过多个线程去处理流中的数据(也可能是主线程顺序执行),这样增加了流的处理方式,也正是因为是不同线程处理的数据,所以顺序发生了变化。
2.2 通过Sream创建流
可以使用stream类提供的of方法,直接返回一个由指定元素组成的流。
Stream<String> stream = Stream.of("a","b","b");
三、Stream的中间操作
Stream的中间操作就像是一个流水线,在一个流上对每一个流过来的数据进行过处理,加工后得到的结果还是一个流。
3.1 filter
filter方法用于通过设置的条件过滤出元素(用于过滤得到一个新的流)。下面举个例子,就是从原来的流串中将b字符串过滤掉。
public static void main(String[] args) {
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();
stream.filter(item->item!="b").forEach(System.out::println);
}
3.2 concat
concat方法将两个Stream流连接在一起,合成一个Stream。若两个输入的Stream都是排序的,则新Stream也是排序的,若输入的Stream中任何一个是并行的,则新的Stream也是并行的,若关闭新的Stream时,则原两个输入的Stream都将执行关闭处理。
Stream.concat(Stream.of(1, 2, 3), Stream.of(4, 5))
.forEach(integer -> System.out.print(integer + " "));
// 1 2 3 4 5
List<String> list1 = Arrays.asList("10", "16", "30");
List<String> list2 = Arrays.asList("15", "25", "50");
Stream<String> stream1 = list1.stream();
Stream<String> stream2 = list2.stream();
// Stream<String> stream2 = list2.parallelStream();
Stream<String> streamResult = Stream.concat(stream1, stream2);
streamResult.forEach(System.out::println);
3.3 map
map方法用于映射每个元素到对应的结果,以下代码片段使用 map 输出了元素拼接一个2,相当于对流中的元素进行一个函数计算。
List<String> list1 = Arrays.asList("10", "16", "30");
list1.stream().map(item-> item+2).forEach(System.out::println);
3.4 flatMap
flatMap方法与map方法类似,都是将原Stream中的每一个元素通过转换函数转换,不同的是,该换转函数的对象是一个Stream,也不会再创建一个新的Stream,而是将原Stream的元素取代为转换的Stream。如果转换函数生产的Stream为null,应由空Stream取代。flatMap有三个对于原始类型的变种方法,分别是:flatMapToInt,flatMapToLong和flatMapToDouble。
List<Integer> list3 = Arrays.asList(1, 2, 3);
list3.stream().flatMap(integer -> Stream.of(integer *3)).forEach(System.out::println);
3.5 peek
peek方法生成一个包含原Stream的所有元素的新Stream,同时会提供一个消费函数(Consumer实例),新Stream每个元素被消费的时候都会执行给定的消费函数,并且消费函数优先执行。
Stream.of(1, 2, 3, 4, 5)
.peek(integer -> System.out.println("accept:" + integer))
.forEach(System.out::println);
// accept:1
// 1
// accept:2
// 2
// accept:3
// 3
// accept:4
// 4
// accept:5
// 5
3.6 limit/skip
limit 返回 Stream 的前面 n 个元素;skip 则是扔掉前 n 个元素。以下代码片段使用 limit 方法保理4个元素:
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
numbers.stream().limit(4).forEach(System.out::println);
//3,2,2,3
3.7 sorted
sorted 方法用于对流进行排序。以下代码片段使用 sorted 方法进行排序:
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
numbers.stream().sorted().forEach(System.out::println);
//2,2,3,3,3,5,7
3.8 distinct
distinct主要用来去重,以下代码片段使用 distinct 对元素进行去重:
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
numbers.stream().distinct().forEach(System.out::println);
//3,2,7,5
四、Stream的最终操作
4.1 forEach
Stream 提供了方法 'forEach' 来迭代流中的每个数据。
4.2 count
count用来统计流中的元素个数。
4.3 collect
collect就是一个归约操作,可以接受各种做法作为参数,将流中的元素累积成一个汇总结果