zoukankan      html  css  js  c++  java
  • java8新特性——Stream API

      Java8中有两大最为重要得改变,其一时Lambda表达式,另外就是 Stream API了。在前面几篇中简单学习了Lambda表达式得语法,以及函数式接口。本文就来简单学习一下Stream API(java.util.stream.*)。

      Stream 是 Java8中处理集合得关键抽象概念,他可以指定你希望对集合进行得操作,可以执行非常复杂得查找、过滤和映射数据等操作。使用Stream API对集合数据进行操作,就类似使用SQL执行得数据库查询。也可以使用S他ream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用得处理数据得方式。

      在Stream操作过程中,可以对数据流做过滤,排序,切片等操作,但是操作之后会产生一个新的流,而数据源则不会发生改变。

    一、什么是 Stream

      Stream是数据渠道,用于操作数据源(集合,数组等)所生成得元素序列。而集合讲得是数据,流讲得是计算。

      注意:

        ①. Stream 自己不会存储元素。

        ②. Stream 不会改变源对象。相反,它会返回一个持有结果得新Stream

        ③. Stream 操作时延迟执行得,这意味着它们会等到需要结果时才执行。(延迟加载)

    二、Stream 操作的三个步骤

      1). 创建 Stream

        一个数据源(集合,数组),获取一个流。

      2). 中间操作

        一个中间操作链,对数据源的数据进行处理。

      3). 终止操作

        一个终止操作,执行中间操作链,并产生结果。

    三、创建Stream 的四种方式

      1). 通过Collection得Stream()方法(串行流)或者 parallelStream()方法(并行流)创建Stream。

     1   /**
     2      * 创建 Stream的四种方式
     3      * 1.通过Collection得Stream()方法(串行流)
     4             或者 parallelStream()方法(并行流)创建Stream
     5      */
     6     @Test
     7     public void test1 () {
     8         
     9         //1. 通过Collection得Stream()方法(串行流)
    10         //或者 parallelStream()方法(并行流)创建Stream
    11         List<String> list = new ArrayList<String>();
    12         Stream<String> stream1 = list.stream();
    13         
    14         Stream<String> stream2 = list.parallelStream();
    15         
    16     }

      2).通过Arrays中得静态方法stream()获取数组流

     1     /**
     2      * 创建 Stream的四种方式
     3      * 2. 通过Arrays中得静态方法stream()获取数组流
     4      */
     5     @Test
     6     public void test2 () {
     7         
     8         //2. 通过Arrays中得静态方法stream()获取数组流
     9         IntStream stream = Arrays.stream(new int[]{3,5});
    10         
    11     }

      3). 通过Stream类中得 of()静态方法获取流

     1     /**
     2      * 创建 Stream的四种方式
     3      * 3. 通过Stream类中得 of()静态方法获取流
     4      */
     5     @Test
     6     public void test3 () {
     7         
     8         //3. 通过Stream类中得 of()静态方法获取流
     9         Stream<String> stream = Stream.of("4645", "huinnj");
    10         
    11     }

      4). 创建无限流(迭代、生成)

     1 /**
     2      * 创建 Stream的四种方式
     3      * 4. 创建无限流(迭代、生成)
     4      */
     5     @Test
     6     public void test4 () {
     7         
     8         //4. 创建无限流
     9         //迭代(需要传入一个种子,也就是起始值,然后传入一个一元操作)
    10         Stream<Integer> stream1 = Stream.iterate(2, (x) -> x * 2);
    11         
    12         //生成(无限产生对象)
    13         Stream<Double> stream2 = Stream.generate(() -> Math.random());
    14         
    15     }

    四、Stream 中间操作

      多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何得处理!而终止操作时一次性全部处理,称为‘延迟加载’

      1). 筛选与切片

        ①. filter —— 接收Lambda ,从流中排除某些元素。

     1     /**
     2      * 筛选与切片
     3      *  filter —— 接收Lambda ,从流中排除某些元素。
     4      *  
     5      */
     6     @Test
     7     public void test5 () {
     8         //内部迭代:在此过程中没有进行过迭代,由Stream api进行迭代
     9         //中间操作:不会执行任何操作
    10         Stream<Person> stream = list.stream().filter((e) -> {
    11             System.out.println("Stream API 中间操作");
    12             return e.getAge() > 30;
    13         });
    14         
    15         //终止操作:只有执行终止操作才会执行全部。即:延迟加载 
    16         stream.forEach(System.out :: println);
    17     
    18     }

      执行上面方法,得到下面结果。

    Person [name=张三, sex=男, age=76]
    Stream API 中间操作
    Stream API 中间操作
    Person [name=王五, sex=男, age=35]
    Stream API 中间操作
    Stream API 中间操作
    Person [name=钱七, sex=男, age=56]
    Stream API 中间操作
    Person [name=翠花, sex=女, age=34]
    

      我们,在执行终止语句之后,一边迭代,一边打印,而我们并没有去迭代上面集合,其实这是内部迭代,由Stream API 完成。

      下面我们来看看外部迭代,也就是我们人为得迭代。

    1   @Test
    2     public void test6 () {
    3         //外部迭代
    4         Iterator<Person> it = list.iterator();
    5         while (it.hasNext()) {
    6             System.out.println(it.next());
    7         }
    8     
    9     }

        ②. limit —— 截断流,使其元素不超过给定数量。

     1     /**
     2      * limit —— 截断流,使其元素不超过给定数量。
     3      */
     4     @Test
     5     public void test7 () {
     6         //过滤之后取2个值
     7         list.stream().filter((e) -> e.getAge() >30 ).
     8         limit(2).forEach(System.out :: println);
     9     
    10     }

      在这里,我们可以配合其他得中间操作,并截断流,使我们可以取得相应个数得元素。而且在上面计算中,只要发现有2条符合条件得元素,则不会继续往下迭代数据,可以提高效率。

      2). 跳过元素

        skip(n),返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空,与limit(n)互补。

     1     /**
     2      * skip(n)—— 跳过元素,返回一个扔掉了前n个元素的流。
     3      * 若流中元素不足n个,则返回一个空,与limit(n)互补。
     4      */
     5     @Test
     6     public void test8 () {
     7         //跳过前2个值
     8         list.stream().skip(2).forEach(System.out :: println);
     9     
    10     }
    11      

       3).  筛选

        distinct 通过流所生成元素的hashCode()和equals()去除重复元素

    1     /**
    2      * distinct —— 筛选,通过流所生成元素的hashCode()和equals()去除重复元素
    3      */
    4     @Test
    5     public void test9 () {
    6         
    7         list.stream().distinct().forEach(System.out :: println);
    8     
    9     }

      注意:distinct 需要实体中重写hashCode()和 equals()方法才可以使用

      4). 映射

        ① . map ,将元素转换成其他形式或者提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。

     1     /**
     2      * map —— 映射 ,将元素转换成其他形式或者提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
     3      */
     4     @Test
     5     public void test10 () {
     6         //将流中每一个元素都映射到map的函数中,每个元素执行这个函数,再返回
     7         List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd");
     8         list.stream().map((e) -> e.toUpperCase()).forEach(System.out::printf);
     9     
    10         //获取Person中的每一个人得名字name,再返回一个集合
    11         List<String> names = this.list.stream().map(Person :: getName).
    12             collect(Collectors.toList());
    13     }

        ② . flatMap —— 接收一个函数作为参数,将流中的每个值都换成一个流,然后把所有流连接成一个流

     1     /**
     2      * flatMap —— 接收一个函数作为参数,将流中的每个值都换成一个流,然后把所有流连接成一个流
     3      */
     4     @Test
     5     public void test11 () {
     6         StreamAPI_Test s = new StreamAPI_Test();
     7         List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd");
     8         list.stream().flatMap((e) -> s.filterCharacter(e)).forEach(System.out::println);
     9         
    10         //如果使用map则需要这样写
    11         list.stream().map((e) -> s.filterCharacter(e)).forEach((e) -> {
    12             e.forEach(System.out::println);
    13         });
    14     }
    15     
    16     /**
    17      * 将一个字符串转换为流
    18      * @param str
    19      * @return
    20      */
    21     public Stream<Character> filterCharacter(String str){
    22         List<Character> list = new ArrayList<>();
    23         for (Character ch : str.toCharArray()) {
    24             list.add(ch);
    25         }
    26         return list.stream();
    27     }

      其实map方法就相当于Collaction的add方法,如果add的是个集合得话就会变成二维数组,而flatMap 的话就相当于Collaction的addAll方法,参数如果是集合得话,只是将2个集合合并,而不是变成二维数组。

      5). 排序

         sorted有两种方法,一种是不传任何参数,叫自然排序,还有一种需要传Comparator 接口参数,叫做定制排序。

     1     /**
     2      * sorted有两种方法,一种是不传任何参数,叫自然排序,还有一种需要传Comparator 接口参数,叫做定制排序。
     3      */
     4     @Test
     5     public void test12 () {
     6         // 自然排序
     7         List<Person> persons = list.stream().sorted().collect(Collectors.toList());
     8         
     9         //定制排序
    10         List<Person> persons1 = list.stream().sorted((e1, e2) -> {
    11             if (e1.getAge() == e2.getAge()) {
    12                 return 0;
    13             } else if (e1.getAge() > e2.getAge()) {
    14                 return 1;
    15             } else {
    16                 return -1;
    17             }
    18         }).collect(Collectors.toList());
    19     }

    五、Stream 终止操作

      1). 查找与匹配

        首先我们先创建一个集合。

     1         List<Person> persons = Arrays.asList(
     2             new Person("张三", "男", 76, Status.FREE),
     3             new Person("李四", "女", 12, Status.BUSY),
     4             new Person("王五", "男", 35, Status.BUSY),
     5             new Person("赵六", "男", 3, Status.FREE),
     6             new Person("钱七", "男", 56, Status.BUSY),
     7             new Person("翠花", "女", 34, Status.VOCATION),
     8             new Person("翠花", "女", 34, Status.FREE),
     9             new Person("翠花", "女", 34, Status.VOCATION)
    10             );

        ①. allMatch —— 检查是否匹配所有元素。

    1     /**
    2      * allMatch —— 检查是否匹配所有元素。
    3      * 判断所有状态是否都是FREE
    4      */
    5     @Test
    6     public void test13 () {
    7          boolean b = persons.stream().allMatch((e) -> Status.FREE.equals(e.getStatus()));
    8          System.out.println(b);
    9     }

        ②. anyMatch —— 检查是否至少匹配所有元素。

    1     /**
    2      * anyMatch —— 检查是否至少匹配所有元素。
    3      * 判断是否有一个是FREE
    4      */
    5     @Test
    6     public void test14 () {
    7          boolean b = persons.stream().anyMatch((e) -> Status.FREE.equals(e.getStatus()));
    8          System.out.println(b);
    9     }

        ③. noneMatch —— 检查是否没有匹配所有元素。

    1     /**
    2      * noneMatch —— 检查是否没有匹配所有元素。
    3      * 判断是否没有FREE
    4      */
    5     @Test
    6     public void test15 () {
    7          boolean b = persons.stream().noneMatch((e) -> Status.FREE.equals(e.getStatus()));
    8          System.out.println(b);
    9     }

        ④. findFirst —— 返回第一个元素。

     1     /**
     2      * findFirst —— 返回第一个元素。
     3      *
     4      */
     5     @Test
     6     public void test16 () {
     7          Optional<Person> person = persons.stream().findFirst();
     8          System.out.println(person);
     9          
    10          person.orElse(new Person("王五", "男", 35, Status.BUSY));
    11     }

      注意:上面findFirst 返回的是一个Optional的对像,他将我们的Person封装了一层,这是为了避免空指针。而且这个对象为我们提供了一个orElse方法,就是当我们得到的这个对象为空时,我们可以传入一个新得对象去替代它。

        ⑤. findAny —— 返回当前流中任意元素。

     1     /**
     2      * findAny —— 返回当前流中任意元素。
     3      */
     4     @Test
     5     public void test17 () {
     6          Optional<Person> person = persons.stream().findAny();
     7          System.out.println(person);
     8          
     9          person.orElse(new Person("王五", "男", 35, Status.BUSY));
    10     }

        ⑥. count —— 返回流中元素总个数。

    1     /**
    2      * count —— 返回流中元素总个数。
    3      */
    4     @Test
    5     public void test18 () {
    6          long count = persons.stream().count();
    7          System.out.println(count);
    8          
    9     }

        ⑦. max —— 返回流中最大值。

    1     /**
    2      * max —— 返回流中最大值。
    3      */
    4     @Test
    5     public void test18 () {
    6         Optional<Person> person = persons.stream().max((e1, e2) -> Double.compare(e1.getAge(), e2.getAge()));
    7          System.out.println(person);
    8          
    9     }

        ⑧. min —— 返回流中最小值。

    1     /**
    2      * min —— 返回流中最小值。
    3      */
    4     @Test
    5     public void test20 () {
    6         Optional<Person> person = persons.stream().min((e1, e2) -> Double.compare(e1.getAge(), e2.getAge()));
    7          System.out.println(person);
    8          
    9     }

      2). 归约(可以将流中元素反复结合在一起,得到一个值)

        ①. reduce(T identitty,BinaryOperator)首先,需要传一个起始值,然后,传入的是一个二元运算。

    1     /**
    2      * reduce(T identitty,BinaryOperator)首先,需要传一个起始值,然后,传入的是一个二元运算。
    3      */
    4     @Test
    5     public void test21 () {
    6         List<Integer> list = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    7         Integer sum = list.stream().reduce(0, (x, y) -> x + y);
    8     }

        ②. reduce(BinaryOperator)此方法相对于上面方法来说,没有起始值,则有可能结果为空,所以返回的值会被封装到Optional中。

    1     /**
    2      *  reduce(BinaryOperator)此方法相对于上面方法来说,没有起始值,则有可能结果为空,所以返回的值会被封装到Optional中
    3      */
    4     @Test
    5     public void test22 () {
    6         List<Integer> list = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    7         Optional<Integer> sum = list.stream().reduce(Integer :: sum);
    8     }

      备注:map和reduce的连接通常称为map-reduce模式,因Google用它来进行网络搜索而出名。

      3). 收集collect(将流转换为其他形式。接收一个Collector接口得实现,用于给其他Stream中元素做汇总的方法)

        Collector接口中方法得实现决定了如何对流执行收集操作(如收集到List,Set,Map)。但是Collectors实用类提供了很多静态方法,可以方便地创建常见得收集器实例。

        ①. Collectors.toList() 将流转换成List

    1     /**
    2      *  Collectors.toList() 将流转换成List
    3      */
    4     @Test
    5     public void test23() {
    6         7         List<String> names = this.list.stream().map(Person :: getName).collect(Collectors.toList());
    8     }

        ②. Collectors.toSet()将流转换为Set

    1     /**
    2      *  Collectors.toSet() 将流转换成Set
    3      */
    4     @Test
    5     public void test24() {
    6         7         Set<String> names = this.list.stream().map(Person :: getName).collect(Collectors.toSet());
    8     }

        ③. Collectors.toCollection()将流转换为其他类型的集合

    1     /**
    2      *  Collectors.toCollection()将流转换为其他类型的集合
    3      */
    4     @Test
    5     public void test25() {
    6         7         LinkedList<String> names =  this.list.stream().map(Person :: getName).collect(Collectors.toCollection(LinkedList :: new));
    8     }

        

        ④. Collectors.counting()  元素个数

    1     /**
    2      *  Collectors.counting()  总数
    3      */
    4     @Test
    5     public void test26() {
    6         List<Integer> list = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    7         Long count =  list.stream().collect(Collectors.counting());
    8     }

        ⑤. Collectors.averagingDouble()、Collectors.averagingDouble()、Collectors.averagingLong() 平均数,这三个方法都可以求平均数,不同之处在于传入得参数类型不同,返回值都为Double

     1     /**
     2      *  Collectors.averagingInt() 、
     3      *   Collectors.averagingDouble()、
     4      *    Collectors.averagingLong() 平均数,
     5      *    者三个方法都可以求平均数,不同之处在于传入得参数类型不同,
     6      *    返回值都为Double
     7      */
     8     @Test
     9     public void test27() {
    10         List<Integer> list = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    11         Double avg =  list.stream().collect(Collectors.averagingInt((x) -> x));
    12     }

        ⑥. Collectors.summingDouble()、Collectors.summingDouble()、Collectors.summingLong() 求和,不同之处在于传入得参数类型不同,返回值为Integer, Double, Long

     1     /**
     2      *  Collectors.summingInt() 、
     3      *   Collectors.summingDouble()、
     4      *    Collectors.summingLong() 求和,
     5      *    者三个方法都可以求总数,不同之处在于传入得参数类型不同,
     6      *    返回值为Integer, Double, Long
     7      */
     8     @Test
     9     public void test28() {
    10         List<Integer> list = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    11         Integer sum =  list.stream().collect(Collectors.summingInt((x) -> x));
    12     }

      ⑦. Collectors.maxBy() 求最大值

    1     /**
    2      * Collectors.maxBy() 求最大值
    3      */
    4     @Test
    5     public void test29() {
    6         List<Integer> list = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    7         Optional<Integer> max =  list.stream().collect(Collectors.maxBy((x, y) ->Integer.compare(x, y)));
    8     }

      ⑧. Collectors.minBy() 求最小值

    /**
         * Collectors.minBy() 求最小值
         */
        @Test
        public void test29() {
            List<Integer> list = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
            Optional<Integer> min =  list.stream().collect(Collectors.minBy((x, y) ->Integer.compare(x, y)));
        }

      ⑨. Collectors.groupingBy()分组 ,返回一个map

    1     /**
    2      * Collectors.groupingBy()分组 ,返回一个map
    3      */
    4     @Test
    5     public void test30() {
    6         Map<String, List<Person>> personMap =  list.stream().collect(Collectors.groupingBy(Person :: getSex));
    7     }

          Collectors.groupingBy()还可以实现多级分组

    1     /**
    2      * Collectors.groupingBy()多级分组 ,返回一个map
    3      */
    4     @Test
    5     public void test31() {
    6         Map<String, Map<Status, List<Person>>> personMap =  list.stream().collect(Collectors.groupingBy(Person :: getSex, Collectors.groupingBy(Person :: getStatus)));
    7     }

      ⑩. Collectors.partitioningBy() 分区,参数中传一个函数,返回true,和false 分成两个区

    1     /**
    2      * Collectors.partitioningBy() 分区,参数中传一个函数,返回true,和false 分成两个区
    3      */
    4     @Test
    5     public void test32() {
    6         Map<Boolean, List<Person>> personMap =  list.stream().collect(Collectors.partitioningBy((x) -> x.getAge() > 30));
    7     }

      上面就是Stream的一些基本操作,只要勤加练习就可以灵活使用,而且效率大大提高。

  • 相关阅读:
    闭包
    作用域
    既然踏足前端,便要立志成为专家
    D3引擎用正则运算的方式,实现智能设备APP消息推送
    基于ArduinoUNOR3的智能调速风扇
    【一起来玩RTOS系列】之RT-Thread Nano快速创建工程
    MCU代码自动生成工具,全面升级
    ESP8266 SOC门磁系统(一)---短信报警功能
    正点原子F407/103,接入机智云,点亮LED
    机智云5.0推出IoT套件GoKit4.0 可实现物联网应用协同开发
  • 原文地址:https://www.cnblogs.com/wuyx/p/9038768.html
Copyright © 2011-2022 走看看