Java8新特性
前言
Java8之前的集合操作不能满足需求的快速发展。所以Steam应运而生。
Stream是对容器对象功能的增强,专注于对容器对象进行各种便利、高效的聚合操作(aggregate operation)或者大批量数据操作。
它提供串行和并行两种模式进行汇聚操作。
stream是一个函数式语言+多核时代综合影响的产物。
清单 2. Java 8 的排序、取值实现
List<Integer> transactionsIds = transactions.parallelStream().
filter(t -> t.getType() == Transaction.GROCERY).
sorted(comparing(Transaction::getValue).reversed()).
map(Transaction::getId).
collect(toList());
一、Stream 总览
1.什么是流
Stream 不是集合元素,它更像一个高级版本的 Iterator。用户只要给出需要对其包含的元素执行什么操作,比如 “过滤掉长度大于 10 的字符串”,Stream 会隐式地在内部进行遍历,做出相应的数据转换。
Stream 就如同一个迭代器(Iterator),单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了,就好比流水从面前流过,一去不复返。
而和迭代器又不同的是,Stream 可以并行化操作。使用并行模式时,数据会被分成多个段,其中每一个都在不同的线程中处理,然后将结果一起输出。
Stream 的另外一大特点是,数据源本身可以是无限的。
2.流的构成
2.1三个基本步骤:
获取一个数据源(source)→ 数据转换→执行操作获取想要的结果,每次转换原有 Stream 对象不改变,返回一个新的 Stream 对象(可以有多次转换),这就允许对其操作可以像链条一样排列,变成一个管道,如下图所示。
2.2有多种方式生成 Stream Source:
从 Collection 和数组
Collection.stream()
Collection.parallelStream()
Arrays.stream(T array) or Stream.of()
从 BufferedReader
静态工厂
自己构建
其它
注:详见参考链接
2.3流的操作类型分为两种:
Intermediate:中间的 :一个流可以后面跟随零个或多个 intermediate 操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。仅仅调用到这类方法,并没有真正开始流的遍历。
Terminal:终点站 :一个流只能有一个 terminal 操作,当这个操作执行后,流就被使用“光”了,无法再被操作。所以这必定是流的最后一个操作。Terminal 操作的执行,才会真正开始流的遍历,并且会生成一个结果,或者一个 side effect。
简单理解Intermediate和Termianl底层:Stream 里有个操作函数的集合,每次转换操作就是把转换函数放入这个集合中,在 Terminal 操作的时候循环 Stream 对应的集合,然后对每个元素执行所有的函数。
还有一种操作被称为 short-circuiting:短路 。
顾名思义,用以短路。用来保证:无限大的Stream,能进行有限的操作。
清单 3. 一个流操作的示例:
int sum = widgets.stream()
.filter(w -> w.getColor() == RED)
.mapToInt(w -> w.getWeight())
.sum();
代码含义: stream() 获取当前小物件的 source,filter 和 mapToInt 为 intermediate 操作,进行数据筛选和转换,最后一个 sum() 为 terminal 操作,对符合条件的全部小物件作重量求和。
二、流的使用详解
简单说,对 Stream 的使用就是实现一个 filter-map-reduce 过程
1.流的构造与转换
清单 4. 构造流的几种常见方法
// 1. Individual values
Stream stream = Stream.of("a", "b", "c");
// 2. Arrays
String [] strArray = new String[] {
"a", "b", "c"};
stream = Stream.of(strArray);
stream = Arrays.stream(strArray);
// 3. Collections
List<String> list = Arrays.asList(strArray);
stream = list.stream();
注:对于基本数值型,目前有三种对应的包装类型 Stream:
清单 5. 数值流的构造
IntStream.of(new int[]{
1, 2, 3}).forEach(System.out::println);
IntStream.range(1, 3).forEach(System.out: