4.2 Stream
新增加的Stream API (java.util.stream)引入了在Java里可以工作的函数式编程。这是目前为止对java库最大的一次功能添加,希望程序员通过编写有效、整洁和简明的代码,能够大大提高生产率。
Stream API让集合处理简化了很多(我们后面会看到不仅限于Java集合类)。让我们从一个简单的类Task开始来看看Stream的用法。
01 | public class Streams { |
06 | private static final class Task { |
07 | private final Status status; |
08 | private final Integer points; |
10 | Task( final Status status, final Integer points ) { |
15 | public Integer getPoints() { |
19 | public Status getStatus() { |
24 | public String toString() { |
25 | return String.format( "[%s, %d]" , status, points ); |
Task类有一个分数的概念(或者说是伪复杂度),其次是还有一个值可以为OPEN或CLOSED的状态.让我们引入一个Task的小集合作为演示例子:
1 | final Collection< Task > tasks = Arrays.asList( |
2 | new Task( Status.OPEN, 5 ), |
3 | new Task( Status.OPEN, 13 ), |
4 | new Task( Status.CLOSED, 8 ) |
第一个问题是所有的开放的Task的点数是多少?在java 8 之前,通常的做法是用foreach迭代。但是Java8里头我们会用Stream。Stream是多个元素的序列,支持串行和并行操作。
2 | final long totalPointsOfOpenTasks = tasks |
4 | .filter( task -> task.getStatus() == Status.OPEN ) |
5 | .mapToInt( Task::getPoints ) |
8 | System.out.println( "Total points: " + totalPointsOfOpenTasks ); |
控制台的输出将会是:
Total points: 18
上面代码执行的流程是这样的,首先Task集合会被转化为Stream表示,然后filter操作会过滤掉所有关闭的Task,接下来使用Task::getPoints 方法取得每个Task实例的点数,mapToInt方法会把Task Stream转换成Integer Stream,最后使用Sum方法将所有的点数加起来得到最终的结果。
在我们看下一个例子之前,我们要记住一些关于Stream的说明。Stream操作被分为中间操作和终点操作。
中间操作返回一个新的Stream。这些中间操作是延迟的,执行一个中间操作比如filter实际上不会真的做过滤操作,而是创建一个新的Stream,当这个新的Stream被遍历的时候,它里头会包含有原来Stream里符合过滤条件的元素。
终点操作比如说forEach或者sum会遍历Stream从而产生最终结果或附带结果。终点操作执行完之后,Stream管道就被消费完了,不再可用。在几乎所有的情况下,终点操作都是即时完成对数据的遍历操作。
Stream的另外一个价值是Stream创造性地支持并行处理。让我们看看下面这个例子,这个例子把所有task的点数加起来。
2 | final double totalPoints = tasks |
5 | .map( task -> task.getPoints() ) |
6 | .reduce( 0 , Integer::sum ); |
8 | System.out.println( "Total points (all tasks): " + totalPoints ); |
这个例子跟上面那个非常像,除了这个例子里使用了parallel()方法 并且计算最终结果的时候使用了reduce方法。
输出如下:
阅读全文