- 由于所在项目组使用的是java8,开发中经常使用到java8的streams API,因此有必要做一个总结。
一、背景
前面学习可以知道,java8中新增加了lambda表达式特性,使编程模式得到大大的简化,而传统的java集合框架面临着大幅度的变化,但是从头实现一个新的集合框架(比如“Collection II”),但取代现有的集合框架是一项非常艰难的工作,因为集合接口渗透了 Java 生态系统的每个角落,将它们一一换成新类库需要相当长的时间。因此,决定采取演化的策略(而非推倒重来)以改进集合 API,在java8总具体做了如下工作:
1、为现有的接口(例如 Collection,List 和 Stream)增加扩展方法;
2、在类库中增加新的 流(stream,即 java.util.stream.Stream)抽象以便进行聚集(aggregation)操作;
3、改造现有的类型使之可以提供流视图(stream view);
4、改造现有的类型使之可以容易的使用新的编程模式,这样用户就不必抛弃使用以久的类库,例如 ArrayList 和 HashMap(当然这并不是说集合 API 会常驻永存,毕竟集合 API 在设计之初并没有考虑到 lambda 表达式。我们可能会在未来的 JDK 中添加一个更现代的集合类库)。
5、提供更加易用的并行(Parallelism)库
二、内部迭代和外部迭代
1、内部迭代
- 集合类库主要依赖于 外部迭代(external iteration)。Collection 实现 Iterable 接口,从而使得用户可以依次遍历集合的元素。
//为每一个shape设置颜色为红色
for (Shape shape : shapes) {
shape.setColor(RED);
}
- 特点:
(1)Java 的 for 循环是串行的,而且必须按照集合中元素的顺序进行依次处理;
(2)集合框架无法对控制流进行优化,例如通过排序、并行、短路(short-circuiting)求值以及惰性求值改善性能。
2、内部迭代
- 使用 内部迭代(internal iteration)替代外部迭代,用户把对迭代的控制权交给类库,并向类库传递迭代时所需执行的代码。
shapes.forEach(s -> s.setColor(RED));
- 特点:
(1)用户把对操作的控制权交还给类库,从而允许类库进行各种各样的优化(例如乱序执行、惰性求值和并行等等)。
(2)内部迭代使得外部迭代中不可能实现的优化成为可能。
总结:
外部迭代同时承担了 做什么(把形状设为红色)和 怎么做(得到 Iterator 实例然后依次遍历)两项职责,而内部迭代只负责 做什么,而把 怎么做 留给类库。通过这样的职责转变:用户的代码会变得更加清晰,而类库则可以进行各种优化,从而使所有用户都从中受益。
三、流Stream
- 流 是 Java SE 8 类库中新增的关键抽象,它被定义于 java.util.stream(这个包里有若干流类型:Stream<T> 代表对象引用流,此外还有一系列特化(specialization)流,比如 IntStream 代表整形数字流)
- 每个流代表一个值序列,流提供一系列常用的聚集操作,使得我们可以便捷的在它上面进行各种运算。集合类库也提供了便捷的方式使我们可以以操作流的方式使用集合、数组以及其它数据结构。
- 案例说明:
Set<Box> hasBlueShape =
shapes.stream()
.filter(s -> s.getColor() == BLUE)
.map(s -> s.getContainingBox())
.collect(Collectors.toSet());
1、在 Collection 上调用 stream() 会生成该集合元素的流视图(stream view)
2、filter() 操作会产生只包含蓝色形状的流
3、map() 操作通过映射函数(这里的映射函数接收一个形状,然后返回包含它的盒子)对输入流里面的
元素进行依次转换,然后产生新流。
4、collect() 操作会把其接收的元素聚集(aggregate)到一起(这里是 Set)collect() 方法的参数
则被用来指定如何进行聚集操作
四、集合和流
- 集合和流尽管在表面上看起来很相似,但它们的设计目标是不同的:集合主要用来对其元素进行有效(effective)的管理和访问(access),而流并不支持对其元素进行直接操作或直接访问,而只支持通过声明式操作在其上进行运算然后得到结果。此外流还有以下特性:
(1)无存储:流并不存储值;流的元素源自数据源(可能是某个数据结构、生成函数或 I/O 通道等等),通过一系列计算步骤得到;
(2)天然的函数式风格(Functional in nature):对流的操作会产生一个结果,但流的数据源不会被修改;
(3)惰性求值:多数流操作(包括过滤、映射、排序以及去重)都可以以惰性方式实现。这使得我们可以用一遍遍历完成整个流水线操作,并可以用短路操作提供更高效的实现;
(4)无需上界(Bounds optional):不少问题都可以被表达为无限流(infinite stream):用户不停地读取流直到满意的结果出现为止(比如说,枚举 完美数 这个操作可以被表达为在所有整数上进行过滤)。集合是有限的,但流不是(操作无限流时我们必需使用短路操作,以确保操作可以在有限时间内完成); - 从API的角度来看,流和集合完全互相独立,不过我们可以既把集合作为流的数据源(Collection 拥有 stream() 和 parallelStream() 方法),也可以通过流产生一个集合(使用前例的 collect() 方法)
五、并行流parallelStream
- 略
六、总结
- Stream 抽象作为新增类库特性的核心,提供了强大的数据集合操作功能,并被深入整合到现有的集合类和其它的 JDK 类型中。
参考资料:
http://zh.lucida.me/blog/java-8-lambdas-inside-out-library-features/
网友评论