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的一些基本操作,只要勤加练习就可以灵活使用,而且效率大大提高。

猜你喜欢

转载自www.cnblogs.com/wuyx/p/9038768.html