美文网首页
【转】Java 8 新增的 Stream 类学习

【转】Java 8 新增的 Stream 类学习

作者: Oliver_Le | 来源:发表于2017-01-16 11:31 被阅读174次

声明:本篇转自《【译】Java 8的新特性—终极版》中的 Streams 相关部分。这里只是为了方便查阅学习。其他详细信息,可参见原文或官方文档


Stream

新增的Stream API(java.util.stream)将生成环境的函数式编程引入了Java库中。这是目前为止最大的一次对Java库的完善,以便开发者能够写出更加有效、更加简洁和紧凑的代码。
Stream API极大得简化了集合操作(后面我们会看到不止是集合),首先看下这个叫Task的类:

public class Streams {
 private enum Status { OPEN, CLOSED };
 private static final class Task {
 private final Status status;
 private final Integer points;
 Task( final Status status, final Integer points ) {
 this.status = status; this.points = points;
 }
 public Integer getPoints() {
 return points;
 }
 public Status getStatus() {
 return status;
 }
 @Override public String toString() {
 return String.format( "[%s, %d]", status, points );
 }
 }
}

Task类有一个分数(或伪复杂度)的概念,另外还有两种状态:OPEN或者CLOSED。现在假设有一个task集合:

final Collection< Task > tasks = Arrays.asList(
 new Task( Status.OPEN, 5 ),
 new Task( Status.OPEN, 13 ),
 new Task( Status.CLOSED, 8 )
 );

首先看一个问题:在这个task集合中一共有多少个OPEN状态的点?在Java 8之前,要解决这个问题,则需要使用foreach循环遍历task集合;但是在Java 8中可以利用steams解决:包括一系列元素的列表,并且支持顺序和并行处理。

// Calculate total points of all active tasks using sum()
final long totalPointsOfOpenTasks = tasks
 .stream()
 .filter( task -> task.getStatus() == Status.OPEN )
 .mapToInt( Task::getPoints )
 .sum();
System.out.println( "Total points: " + totalPointsOfOpenTasks );

运行这个方法的控制台输出是:

Total points: 18

这里有很多知识点值得说。首先,tasks集合被转换成stream表示;其次,在stream上的filter操作会过滤掉所有CLOSED的task;第三,mapToInt操作基于每个task实例的Task::getPoints方法将task流转换成Integer集合;最后,通过sum方法计算总和,得出最后的结果。

在学习下一个例子之前,还需要记住一些streams(点此更多细节)的知识点。Stream之上的操作可分为中间操作和晚期操作。

中间操作会返回一个新的steam——执行一个中间操作(例如filter)并不会执行实际的过滤操作,而是创建一个新的stream,并将原stream中符合条件的元素放入新创建的stream。

晚期操作(例如forEach或者sum),会遍历stream并得出结果或者附带结果;在执行晚期操作之后,stream处理线已经处理完毕,就不能使用了。在几乎所有情况下,晚期操作都是立刻对steam进行遍历。

stream的另一个价值是创造性地支持并行处理(parallel processing)。对于上述的tasks集合,我们可以用下面的代码计算所有任务的点数之和:

// Calculate total points of all tasks
final double totalPoints = tasks
 .stream()
 .parallel()
 .map( task -> task.getPoints() ) // or map( Task::getPoints )
 .reduce( 0, Integer::sum );
System.out.println( "Total points (all tasks): " + totalPoints );

这里我们使用parallel方法并行处理所有的task,并使用reduce方法计算最终的结果。控制台输出如下:

Total points(all tasks): 26.0

对于一个集合,经常需要根据某些条件对其中的元素分组。利用steam提供的API可以很快完成这类任务,代码如下:

// Group tasks by their status
final Map< Status, List< Task > > map = tasks
 .stream()
 .collect( Collectors.groupingBy( Task::getStatus )
 );
System.out.println( map );

控制台的输出如下:

{CLOSED=[[CLOSED, 8]], OPEN=[[OPEN, 5], [OPEN, 13]]}

最后一个关于tasks集合的例子问题是:如何计算集合中每个任务的点数在集合中所占的比重,具体处理的代码如下:

// Calculate the weight of each tasks (as percent of total points)
 final Collection< String > result = tasks
 .stream() // Stream< String > 
 .mapToInt( Task::getPoints ) // IntStream
 .asLongStream() // LongStream
 .mapToDouble( points -> points / totalPoints ) // DoubleStream     
 .boxed() // Stream< Double >
 .mapToLong( weigth -> ( long )( weigth * 100 ) ) // LongStream 
 .mapToObj( percentage -> percentage + "%" ) // Stream< String>  
 .collect( Collectors.toList() ); // List< String > 
System.out.println( result );

控制台输出结果如下:

[19%, 50%, 30%]

最后,正如之前所说,Steam API不仅可以作用于Java集合,传统的IO操作(从文件或者网络一行一行得读取数据)可以受益于steam处理,这里有一个小例子:

final Path path = new File( filename ).toPath();
try( Stream< String > lines = Files.lines( path, StandardCharsets.UTF_8 ))
 { lines.onClose( () -> System.out.println("Done!") ).forEach( System.out::println );}

Stream的方法onClose 返回一个等价的有额外句柄的Stream,当Stream的close()方法被调用的时候这个句柄会被执行。Stream API、Lambda表达式还有接口默认方法和静态方法支持的方法引用,是Java 8对软件开发的现代范式的响应。

相关文章

网友评论

      本文标题:【转】Java 8 新增的 Stream 类学习

      本文链接:https://www.haomeiwen.com/subject/ojaubttx.html