• Upgrading to Java 8——第四章 The Stream API


      在这章中我们将学习Stream API,在JDK 8 中的一项新的特性。为了理解这一章的主题,你需要知道如何使用Lambda表达式和java.util.function里的预定义的函数式接口。

      一个Stream 类似于一个管道,但它里面运输的不是水和石油,而是把数据从源头运输到目的地。根据传递的方式,一个stream可以是并行和并发的。并行的stream运行在多核的CPU的机器上会很有用。

      乍一看,一个stream就像是一个集合容器,但是,它不是一个数据结构用来存储对象,它只是负责移动对象,所以,你不能把它想象成集合对象那样往它里面添加数据。

      使用stream的主要原因是它支持并行和并发的聚合操作。例如,你可以非常容易地从stream里面过滤,排序或映射元素。

      Stream API的不同的类型在java.util.stream包中。其中Stream接口是这里面最常用的stream类型。 一个Stream可以传递任何类型的对象,同时也有几个特殊化的Stream:IntStream, LongStream and DoubleStream。他们都来源于BaseStream。

      下面的表格展示了一些在Stream接口中常见的方法:

    方法   描述
    concat  懒加载的方式连接两个stream。返回一个新的stream,他的元素包括两个stream的所有元素。第一个stream的元素后面紧跟着第二个stram的元素。
    count  返回stream里面元素的个数。
    empty  创建并返回一个空的stream。
    filter  在stream所有的元素中根据给定的断言接口返回一个新的stream。
    forEach  给stream每个元素执行一个操作。
    limit  从当前的stream中根据指定最大元素的个数返回一个新的stream。
    map  返回包含了应用于stream的元素的给定的方法的的结果的stream。
    max  根据比较器返回stream中最大的元素。
    min  根据比较器返回stream中最小的元素。
    of  返回一个已经给定了值的stream。
    reduce  在stream上使用唯一ID和累加器执行递减操作。
    sorted  返回一个新的使用自然排序的stream。
    toArray  返回一个包含stream所有元素的数组。

    有些stream的方法执行中间过程的操作,有的执行最终的操作。中间过程的操作会把一个stream传输到另一个stream中。像filter,Map,sorted等这些方法。

    执行最终操作的方法会产生结果或是其他的影响。例如,count,forEach就是执行的最终结果的操作。

    中间过程的操作属于懒加载的方式,他不会真正的执行,只有是执行最终结果的才会真正在源上开始计算。

    创建和获取一个Stream

    你可以使用Stream中静态的of方法来创建一个连续的stream。例如,下面的例子就是创建了一个包含三个Integer类型元素的stream。

      Stream<Integer> stream = Stream.of(100, 200, 300);

    或者,给of方法传递一个数组:

      String[] names = {"Bart", "Lisa", "Maggie"};
      Stream<String> stream = Stream.of(names);

    现在java.util.Arrays 帮助类已经有了method方法用来转换一个数组给stream。例如,你可以重写上面的代码,使用Arrays类创建一个stream。

      String[] names = {"Bart", "Lisa", "Maggie"};
      Stream<String> stream = Arrays.stream(names);

    另外,在java.util.Collectiond接口中也有个了默认的stream和parallelStream方法分别用来返回一个顺序的stream和并行的stream。签名如下:

      default java.util.stream.Stream<E> stream()
      default java.util.stream.Stream<E> parallelStream()

    多亏了Collection接口中的这些方法,从List或Set中获取stream简直小菜一碟。

    除此而外,在java.nio.file.Files类中提供了两个返回Stream<Path>的方法:list和walk。list方法返回一个指定路径的入口的泛型为Path的stream。walk方法遍历了给定路径下入口里所有的文件并作为stream返回。

    Files 也包含了lines方法返回泛型为String的stream的所有行的文本。

    看下面的例子。

    import java.io.IOException;
    import java.nio.file.FileVisitOption;
    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.nio.file.Paths;
    import java.util.stream.Stream;
    
    public class ObtainStreamDemo {
    
        public static void main(String[] args) throws IOException {
        Path path = Paths.get(".");
        
        // use list method.
        Stream<Path> list = Files.list(path);
        list.forEach(System.out::println);
        list.close();
        
        System.out.println("===========================================");
        
        // use walk method.
        Stream<Path> walk = Files.walk(path, FileVisitOption.FOLLOW_LINKS);
        walk.forEach(System.out::println);
        walk.close();
        
        }
    
    }


    连接两个stream

    在Stream接口中提供了concat方法用来以懒加载的方式连接两个stream。这个方法返回一个新的stream,它的元素是两个stream的所有元素,并且第二个stream的元素接在第一个stream元素的后面。

    看下面的例子。

    import java.util.stream.Stream;
    
    public class StreamConcatDemo {
    
        public static void main(String[] args) {
        Stream<String> stream1 = Stream.of("January", "Christie");
        Stream<String> stream2 = Stream.of("Okanagan", "Sydney", "Alpha");
    
        Stream.concat(stream1, stream2).sorted().forEach(System.out::println);
    
        }
    
    }

    需要注意的是,此方法不会剔除重复的元素,如果有相同的元素,都一并连接在一个新的stream中。

     过滤。

    当你从stream中基于一定的条件过滤该stream并返回一个新的包含选定的元素的stream。你可以在Stream对象上调用filter方法,并传递一个predicate函数式接口,由它来决定哪些元素包含在新的stream中。

    filter方法的签名如下:

      Stream<T> filter(java.util.function.Predicate<? super T> predicate)

    下面的例子,从exapmle.txt中读取文件,并过滤掉注释行(已“#”开头的)和空白行。

    public class StreamFilterDemo1 {
        public static void main(String[] args) {
            Predicate<String> notCommentOrEmptyLine
                    = (line) -> line.trim().length() > 0
                    && !line.trim().startsWith("#");
            try (FileReader fr = new FileReader("example.txt");
                    BufferedReader br = new BufferedReader(fr)) {
                Stream<String> lines = br.lines();
                lines.filter(notCommentOrEmptyLine)
                        .forEach(System.out::println);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    example.txt:

    # Set path so it includes user's private bin if it exists
    if [ -d "$HOME/bin" ] ; then
        PATH="$HOME/bin:$PATH"
    
    fi

    执行结果如下:
    if [ -d "$HOME/bin" ] ; then
        PATH="$HOME/bin:$PATH"
    fi

    第二个例子是使用stream实现在你机器上的文件搜索。为了精确些,代码只显示在给定的目录和子目录下后缀名为java的文件。

    import java.io.IOException;
    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.nio.file.Paths;
    import java.util.stream.Stream;
    
    public class StreamFilterDemo2 {
        public static void main(String[] args) {
            // find all java files in the parent directory and
            // all its subdirectories
            Path parent = Paths.get("..");
            try {
                Stream<Path> list = Files.walk(parent);
                list.filter((Path p) -> p.toString().endsWith(".java"))
                        .forEach(System.out::println);
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }

     StreamFilterDemo2 类开始从当前目录的父目录开始执行,它传递了Path给Files.walk方法去获取泛型为Paths的stream,接着根据predicate接口只包含后缀名为.java的文件,并用forEach遍历打印。

  • 相关阅读:
    python中的scapy模块
    延时注入跨库注入
    Linux 技巧:让进程在后台可靠运行的几种方法
    爆破phpmyadmin小脚本
    ACID原则
    MYSQL批量插入数据库实现语句性能分析
    MYSQL批量插入数据库实现语句性能分析
    JS数组去重算法
    JS数组去重算法
    JS中substr和substring的用法和区别
  • 原文地址:https://www.cnblogs.com/IcanFixIt/p/4325165.html
Copyright © 2020-2023  润新知