美文网首页编程杂货铺
Java8新特性Stream

Java8新特性Stream

作者: 会动的木头疙瘩儿 | 来源:发表于2018-10-10 14:52 被阅读4次

    声明转载自:http://www.importnew.com/11908.html#streams

    Stream API极大简化了集合框架的处理:

    package com.spring.nahong.swagger2.swaggerdoc.test;
    
    import java.util.Arrays;
    import java.util.Collection;
    
    public class Streams  {
        private enum Status {
            OPEN, CLOSED
        }
    
    
        public static void main(String[] args) {
            final Collection< Task > tasks = Arrays.asList(
                    new Task( Status.OPEN, 5 ),
                    new Task( Status.OPEN, 13 ),
                    new Task( Status.CLOSED, 8 )
            );
    
            final long totalPointsOfOpenTasks = tasks
                    .stream()
                    .filter( task -> task.getStatus() == Status.OPEN )
                    .mapToInt( Task::getPoints )
                    .sum();
    
            System.out.println( "Total points: " + totalPointsOfOpenTasks );
        }
    
    
        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类,里面有状态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 )
            );
    

    我们下面要讨论的第一个问题是所有状态为OPEN的任务一共有多少分数?在Java 8以前,一般的解决方式用foreach循环,但是在Java 8里面我们可以使用stream:一串支持连续、并行聚集操作的元素。

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

    执行程序控制台打印结果:

    Total points: 18
    

    这里有几个注意事项。第一,task集合被转换化为其相应的stream表示。然后,filter操作过滤掉状态为CLOSED的task。下一步,mapToInt操作通过Task::getPoints这种方式调用每个task实例的getPoints方法把Task的stream转化为Integer的stream。最后,用sum函数把所有的分数加起来,得到最终的结果。

    在继续讲解下面的例子之前,关于stream有一些需要注意的地方(详情在这里).stream操作被分成了中间操作与最终操作这两种。

    中间操作返回一个新的stream对象。中间操作总是采用惰性求值方式,运行一个像filter这样的中间操作实际上没有进行任何过滤,相反它在遍历元素时会产生了一个新的stream对象,这个新的stream对象包含原始stream
    中符合给定谓词的所有元素。

    像forEach、sum这样的最终操作可能直接遍历stream,产生一个结果或副作用。当最终操作执行结束之后,stream管道被认为已经被消耗了,没有可能再被使用了。在大多数情况下,最终操作都是采用及早求值方式,及早完成底层数据源的遍历。

    stream另一个有价值的地方是能够原生支持并行处理。让我们来看看这个算task分数和的例子。

    public static void main(String[] args) {
    
            final Collection< Task > tasks = Arrays.asList(
                    new Task( Status.OPEN, 5 ),
                    new Task( Status.OPEN, 13 ),
                    new Task( Status.CLOSED, 8 )
            );
    
            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 );
        }
    

    这个例子和第一个例子很相似,但这个例子的不同之处在于这个程序是并行运行的,其次使用reduce方法来算最终的结果。
    下面是这个例子在控制台的输出:

    Total points (all tasks): 26.0
    

    经常会有这样的需求:我们需要按照某种准则来对集合中的元素进行分组。Stream也可以处理这样的需求,下面是一个例子:

    public static void main(String[] args) {
    
            final Collection< Task > tasks = Arrays.asList(
                    new Task( Status.OPEN, 5 ),
                    new Task( Status.OPEN, 13 ),
                    new Task( Status.CLOSED, 8 )
            );
    
            final Map< Status, List< Task > > map = tasks
                    .stream()
                    .collect( Collectors.groupingBy( Task::getStatus ) );
            System.out.println( map );
        }
    

    控制台输出:

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

    让我们来计算整个集合中每个task分数(或权重)的平均值来结束task的例子

    public static void main(String[] args) {
    
            final Collection< Task > tasks = Arrays.asList(
                    new Task( Status.OPEN, 5 ),
                    new Task( Status.OPEN, 13 ),
                    new Task( Status.CLOSED, 8 )
            );
    
            final double totalPoints = tasks
                    .stream()
                    .parallel()
                    .map( task -> task.getPoints() ) // or map( Task::getPoints )
                    .reduce( 0, Integer::sum );
    
    
            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%]
    

    最后,就像前面提到的,Stream API不仅仅处理Java集合框架。像从文本文件中逐行读取数据这样典型的I/O操作也很适合用Stream API来处理。下面用一个例子来应证这一点。

    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()方法时,与关闭相关的处理器就会执行。

    相关文章

      网友评论

        本文标题:Java8新特性Stream

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