zoukankan      html  css  js  c++  java
  • Java8的心特性-集合的流式编程

    1、集合流的简介

    1.1 集合的流式编程的简介

    Stream是JDK1.8之后出现的新特性,也是JDK1.8新特性中最值得学习的两种特性之一。(另一个是Lambda表达式)。

    Stream是对集合操作的增强,流不是集合的元素,不是一种数据结构,不负责数据的存储。流更像是一个迭代器,可以单向的遍历一个集合中的每一个元素,并且不可循环(废话,单向还怎么循环)。

    1.2 为什么要使用集合的流式编程

    有些时候,对集合中的元素进行操作的时候,需要使用到其他操作的结果。在这个过程中,集合的流式编程可以大幅度的简化代码的数量,将数据源中的数据,读取到一个流中,可以对这个流中的数据进行操作(删除、过滤、映射...),每次的操作结果也是一个流对象,可以对这个流再进行其他的操作。

    1.3 使用流式编程的步骤

     通常情况下,对集合中的数据使用流式编程,需要经过以下三步:

    1. 获取数据源,将数据源中的数据读取到流中。
    2. 对流中的数据进行各种各样的处理。
    3. 对流中的数据进行整合处理。

    在上述三个过程中,过程2,有若干方法,可以对流中的数据进行各种各样的操作,并且返回流对象本身,这样的操作,被称为-中间操作,过程2中,有若干方法,可以对流中的数据进行各种处理,并关闭流,这样的操作,被称为-最终操作。

    在中间操作和最终操作中,基本上所有的方法参数都是函数式接口,可以使用lambda表达式来实现,使用集合的流式编程,来简化代码量,是需要对lambda表达式做到熟练掌握。

    2、数据源的获取

    2.1 数据源的简介

    数据源,顾名思义,即是流中的数据的来源,是集合的流式编程的第一步,将数据源中的数据读取到流中,进行处理,注意:将数据读取到流中进行处理的时候,与数据源中的数据没有关系。也就是说,中间操作对流中的数据进行处理、过滤、映射、排序...此时是不会影响数据源中的数据的。

    2.2 数据源的获取

    这个过程,其实是将一个容器中的数据,读取到一个流中。因此无论什么容器作为数据源,读取到流中的方法返回值一定是一个Stream。

    package stream;
    
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.stream.IntStream;
    import java.util.stream.Stream;
    public class DataSourceDemo {
        // 数据源的获取
        public static void main(String[] args) {
            // 1、集合的流失编程
            ArrayList<Integer> list = new ArrayList<>();
    
            // 2、Collection流的获取
            Stream<Integer> stream = list.stream();
            Stream<Integer> stream2 = list.parallelStream();
    
            // 3、Array流的获取(这个没有并行流)
            int[] array = {1,2,3,4,5};
            IntStream stream3 = Arrays.stream(array);
        }
    }

    关于stream()和parallelStream

    他们都是Collection集合获取数据源的方法,不同点在于stream()方法获取的数据源是串行的,parallelStream()获取的数据源是并行的。parallelStream()内部集成了多个线程对流中的数据进行操作,效率更高。

    3、最终操作

    3.1 最终操作的简介

    将流中的数据整合到一起,可以存入一个集合,也可以直接对流中的数据进行遍历、数据统计...通过最终操作,需要掌握如何从流中提取出来玩吗想要的信息。

    注意事项:最终操作,之所以叫最终操作,是因为,在最终操作执行结束后,会关闭这个流,流中的所有数据都会被销毁,如果使用一个已经关闭了的流,会出现异常。

    3.2 collect

    将流中的数据收集到一起,对这些数据进行处理。最常见的处理,就是讲流中的数据存入一个集合。collect方法的参数,是一个Collector接口,而且这个接口并不是一个函数式接口。实现这个接口,可以自定义收集规则,但是,绝大部分情况下,不需要自定义。我们可以使用Collectors工具类提供的方法。

    直接使用Collectors工具类提供的方法即可。

    package stream;
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    import java.util.Map;
    import java.util.Set;
    import java.util.stream.Collectors;
    public class FinalOperationDemo {
        //最终操作
        public static void main(String[] args) {
            ArrayList<Integer> list = new ArrayList<>();
            Collections.addAll(list,10,20,30,40,50,60,70,80,90,5,15,25,35,45,55,65,75,86,95,100);
    
            // 1、collect
            // 1.1 转成list
            List<Integer> result1 = list.stream().collect(Collectors.toList());
            System.out.println(result1);
    
            // 1.2 转成set
            Set<Integer> result2 = list.stream().collect(Collectors.toSet());
            System.out.println(result2);
    
            //1.3 转成map,需要提供两个函数式接口的实现,分别实现键和值的生成规则。如果ele/10会报错,因为key重复了。
            Map<Integer, Integer> result3 = list.stream().collect(Collectors.toMap(ele -> ele + 10, ele -> ele));
            System.out.println(result3);
    
            // 1.4 转成Collection,这个用的少,而且还要传参数(转换规则),因为你都转成list和set了,还转Collection干什么
        }
    }

    3.3 reduce

    将流中的数据按照一定的规则聚合起来。

    // 2、reduce
    
    // 将流中的元素,逐一带入到这个方法中,进行运算
    // 最终的运算结果,得到的其实是一个Optional类型,需要使用get()获取到里面的数据。
    Integer result4 = list.stream().reduce((e1, e2) -> e1 + e2).get();
    System.out.println(result4);

    3.4 count

    统计流中的元素数量

    // 3、count
    long result5 = list.stream().count();
    System.out.println(result5);

    3.5 forEach

    迭代、遍历流中的数据。

    // 4、forEach
    list.stream().forEach(System.out::println);

    3.6 max & min

    获取流中的最大的元素、最小的元素(结果也是Optional类型,需要用get方法获取值)

    // 5、max & min
    // 获取最大值
    Integer result6 = list.stream().max(Integer::compareTo).get();
    System.out.println("max is : " + result6);
    // 获取最小值
    Integer result7 = list.stream().min(Integer::compareTo).get();
    System.out.println("min is : " + result7);

    3.7 Matching

    allMatch:只有当流中所有的元素,都匹配指定的会泽,才会返回true;

    anyMatch:只要流中任意的数据,满足指定的规则,就会返回true;

    noneMatch:只有当流中的所有的元素,都不满足指定的规则,才会返回true。

    // 6、Matching
    // 判断流中是否所有的元素都大于 50
    boolean result8 = list.stream().allMatch(ele -> ele > 50);
    System.out.println(result8);
    
    // 判断流中是否有大于 50 的数据
    boolean result9 = list.stream().anyMatch(ele -> ele > 50);
    System.out.println(result9);
    
    // 判断流中是否没有奇数
    boolean result10 = list.stream().noneMatch(ele -> ele % 2 != 0);
    System.out.println(result10);

    3.8 find

    当我们第一眼看到这个”find“单词的时候,想到的就是按照一定规则获取一定的元素。其实不是这样。关于通过一定规则获取到(过滤掉一些得到其他)一定的元素是“中间操作”中的方法,后面会学到。

    findFirst:从流中获取一个元素(一般情况下,是获取的开头的元素)

    findAny:从流中获取一个元素(一般情况下,是获取的开头的元素)

    这两个方法,绝大部分情况下,是完全相同的,但是在多线程的环境下,findAny和findFirst返回的结果可能不一样。

    // 8、find
    Optional<Integer> result11 = list.stream().findFirst();
    System.out.println(result11);
    
    Optional<Integer> result12 = list.stream().findAny();
    System.out.println(result12);
    
    Optional<Integer> result13 = list.parallelStream().findFirst();
    System.out.println(result13);
    
    Optional<Integer> result14 = list.parallelStream().findAny();
    System.out.println(result14);
    //运行结果,find的运行结果是Optional,如果想得到具体值,可以用get()方法
    Optional[10]
    Optional[10]
    Optional[10]
    Optional[35]
    //这里可以看到在多线程(并行流下)结果是不确定的,有可能意义,有可能不一样。

    3.9 最终操作的注意事项

    最终操作,会关闭流。如果一个流被关闭了,再去使用这个流,就会出现异常。

    // 9、最终操作错误示范
    Stream<Integer> stream = list.stream();
    long count = stream.count();
    stream.forEach(System.out::println);
    // 异常信息
    // Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed

    全部代码

    package stream;
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    import java.util.Map;
    import java.util.Optional;
    import java.util.Set;
    import java.util.stream.Collectors;
    import java.util.stream.Stream;
    
    public class FinalOperationDemo {
        //最终操作
        public static void main(String[] args) {
            ArrayList<Integer> list = new ArrayList<>();
            Collections.addAll(list,10,20,30,40,50,60,70,80,90,5,15,25,35,45,55,65,75,86,95,100);
    
            // 1、collect
            // 1.1 转成list
            List<Integer> result1 = list.stream().collect(Collectors.toList());
            System.out.println(result1);
    
            // 1.2 转成set
            Set<Integer> result2 = list.stream().collect(Collectors.toSet());
            System.out.println(result2);
    
            //1.3 转成map,需要提供两个函数式接口的实现,分别实现键和值的生成规则。如果ele/10会报错,因为key重复了。
            Map<Integer, Integer> result3 = list.stream().collect(Collectors.toMap(ele -> ele + 10, ele -> ele));
            System.out.println(result3);
    
            // 1.4 转成Collection,这个用的少,而且还要传参数(转换规则),因为你都转成list和set了,还转Collection干什么
    
            // 2、reduce
    
            // 将流中的元素,逐一带入到这个方法中,进行运算
            // 最终的运算结果,得到的其实是一个Optional类型,需要使用get()获取到里面的数据。
            Integer result4 = list.stream().reduce((e1, e2) -> e1 + e2).get();
            System.out.println(result4);
    
            // 3、count
            long result5 = list.stream().count();
            System.out.println(result5);
    
            // 4、forEach
            list.stream().forEach(System.out::println);
    
            // 5、max & min
            // 获取最大值
            Integer result6 = list.stream().max(Integer::compareTo).get();
            System.out.println("max is : " + result6);
            // 获取最小值
            Integer result7 = list.stream().min(Integer::compareTo).get();
            System.out.println("min is : " + result7);
    
            // 6、Matching
            // 判断流中是否所有的元素都大于 50
            boolean result8 = list.stream().allMatch(ele -> ele > 50);
            System.out.println(result8);
    
            // 判断流中是否有大于 50 的数据
            boolean result9 = list.stream().anyMatch(ele -> ele > 50);
            System.out.println(result9);
    
            // 判断流中是否没有奇数
            boolean result10 = list.stream().noneMatch(ele -> ele % 2 != 0);
            System.out.println(result10);
    
            // 8、find
            Optional<Integer> result11 = list.stream().findFirst();
            System.out.println(result11);
    
            Optional<Integer> result12 = list.stream().findAny();
            System.out.println(result12);
    
            Optional<Integer> result13 = list.parallelStream().findFirst();
            System.out.println(result13);
    
            Optional<Integer> result14 = list.parallelStream().findAny();
            System.out.println(result14);
    
            // 9、最终操作错误示范
            Stream<Integer> stream = list.stream();
            long count = stream.count();
            stream.forEach(System.out::println);
        }
    }

    4、中间操作

    4.1 中间操作的简介

    将数据从数据源中读取到流中,中间操作,就是对流中的数据进行各种各样的操作、处理,中间操作可以连续操作,每一个操作的返回值都是一个Stream对象,可以继续进行其他的操作,直到最终操作。

    4.2 filter

    条件过滤,仅保留流中满足指定条件的数据,其他不满足的数据都会被删除。这么一想,参数肯定是Predicate。

    // 2、条件过滤
    list.stream().filter(ele -> ele.length() > 5).forEach(System.out::println);

    4.3 distinct

    去重集合中重复的元素,这个方法没有参数,去重的规则与HashSet相同。

    那HashSet的去重规则是什么??如果HashCode不一样,则不同;如果一样,则比较equals方法,如果不一样,则不同,即是不同元素;如果一样,则相同,是重复元素,去重。

    因此如果这个流中的数据是自定义的类型等,则需要实现这两个方法,方能进行去重操作。

    // 3、去重
    list.stream().distinct().forEach(System.out::println);

    4.4 sorted

    将流中的数据进行排序(这里排序有两种:一种是有参数[Comparator],指定排序规则;一种是无参,按照流中元素类实现的Comparable接口中的comparato方法。)

    // 4、排序(一个是有参[Comparator接口],一个数无参)
    // 按照流中的元素对应的类,实现的Comparable接口中的方法实现排序
    list.stream().sorted().forEach(System.out::println);
    // 对流中的数据,按照指定的规则进行排序
    list.stream().sorted((e1,e2) -> e1.length() - e2.length()).forEach(System.out::println);

    4.5 limit & skip

    limit:限制,截取流中指定数量的元素

    skip:跳过,跳过流中的指定数量的元素

    通常配合使用

    // 5、跳过或截断(通常配合使用)
    // 跳过开头指定的元素
    list.stream().skip(3).forEach(System.out::println);
    // 截断指定数量的元素
    list.stream().limit(3).forEach(System.out::println);
    // 配合使用,截取部
    list.stream().skip(5).limit(2).forEach(System.out::println);

    4.6 map & flatMap

    看到这个map,大家不要以为是要将流中的数据变成一个键值对的Map,那是最终操作collection(Colletors.toMap(key映射关系,value映射关系))方法的内容。

    map:对流中的数据进行映射,用新的数据替换旧的数据(而且新的数据可以和原有数据不是一个类型,甚至可以一个元素变成一列数组)。

    // 6、map & faltMap
    list.stream().map(ele -> ele + ".txt").forEach(System.out::println);

    map最主要的作用就是用来做元素的替换(换成另一个元素,或者一组元素),其实map是一个元素的映射。

    flatMap也是元素的映射,flatMap是扁平化映射。

    package stream;
    import java.util.Arrays;
    public class FlapMap {
        public static void main(String[] args) {
            String[] strs = {"hello","world"};
            /*
            * 映射之前,流中有两个元素,是两个字符串
            * 映射完成后,流中依然是两个元素,是两个char[]
            * 最后forEach遍历的,其实是两个char[]
            * */
            //Arrays.stream(strs).map(ele -> ele.toCharArray()).forEach(ele -> System.out.println(Arrays.toString(ele)));
            Arrays.stream(strs).map(String::toCharArray).forEach(ele -> System.out.println(Arrays.toString(ele)));
    
            /*
            * flatMap:扁平化映射,将流中的容器中的元素直接取出,直接存放在流中。
            * */
            Arrays.stream(strs).map(ele -> ele.split("")).flatMap(Arrays::stream).forEach(System.out::println);
        }
    }

    4.7 mapToInt

    将流中的数据替换成int类型,此时得到IntStream结果。

    package stream;
    
    import java.util.ArrayList;
    import java.util.Collections;
    public class MapDemo {
        public static void main(String[] args) {
            // 1、准备一个集合,作为数据源
            ArrayList<Person> list = new ArrayList<Person>();
            Collections.addAll(list,new Person("xiaoming",12),new Person("laowang",13),new Person("xiaobai",11));
    
            // 2、如果现在没有流式编程,让你返回所有学生的最大年龄,最小年龄,平均年龄。
            // 如果可以用流
            Integer sum = list.stream().map(Person::getAge).reduce((e1, e2) -> e1 + e2).get();
            System.out.println(sum);
            // 如果是平均,你还要用count配合计算
    
            // 现在这些我们都不用
            // 2、mapToInt:将流中的数据,映射成int类型的数据,此时,这个方法的返回值不再是Stream,而是IntStream
            // 其实对于上面的注释,映射为int类型没什么厉害的,map就可以,但是呢?但是呢?这里的厉害之处是返回结果已经不再是Stream
            // 结果是IntStream,而IntStream里面有很多管局数的操作,比如最大,最小,平均,求和等等。
            double avg = list.stream().mapToInt(Person::getAge).average().getAsDouble();
            System.out.println(avg);
        }
    }
    class Person{
        String name;
        int age;
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
        public int getAge() {
            return age;
        }
    }

    6、综合案例

    需求:一个集合中存储了若干个Student对象,要求查询出以下结果:

    1、所有及格的学生信息

    2、所有及格的学生姓名

    3、所有学生的平均信息

    4、班级的前3名(按照成绩)

    5、班级的3-10名(按照成绩)

    6、所有不及格的学生平均成绩

    7、将及格的学生,按照成绩降序输出所有信息

    8、班级学生总分。

    暂时略。。。

  • 相关阅读:
    python--DAY7面向对象进阶
    python--socket实例
    Python---day5-各类模块的使用
    python--day4--迭代器、生成器
    Python--三元运算与lambda表达式
    python--函数式登录程序
    Python--变量作用域
    Python--函数
    Python----文件的IO操作
    swagger2文档的步骤
  • 原文地址:https://www.cnblogs.com/G-JT/p/14001451.html
Copyright © 2011-2022 走看看