• Java 8 (3) Stream 流


    什么是流?

      流是Java API的新成员,它允许你以声明性方式处理数据集合(通过查询语言来表达,而不是临时编写一个实现)。就现在来说你可以先把它当做是一个遍历数据集的高级迭代器。此外,流还支持并行,你无需写任何多线程的代码!

    定义一个实物类

    public class Dish {
        //菜名
        private String Name;
        //是否素食
        private boolean vegetarian;
        //热量
        private Integer Calories;
        //分类
        private Type type;
    }
    
    public enum Type {
        MEAT,//肉
        FISH,//鱼
        OTHER//其他
    }

    定义一个menu集合,以下栗子基于此menu

            List<Dish> menu = Arrays.asList(
                    new Dish("猪肉炖粉条",false,800,Type.MEAT),
                    new Dish("小炒牛肉",false,700,Type.MEAT),
                    new Dish("宫保鸡丁",false,400,Type.MEAT),
                    new Dish("地三鲜",true,530,Type.OTHER),
                    new Dish("水煮菠菜",true,350,Type.OTHER),
                    new Dish("拔丝地瓜",true,120,Type.OTHER),
                    new Dish("火山下雪",true,550,Type.OTHER),
                    new Dish("水煮鱼",false,330,Type.FISH),
                    new Dish("于是乎",false,450,Type.FISH)
            );

    举个栗子:返回热量低的菜肴名称

    //java7
    
            List<Dish> lowCaloricDishes = new ArrayList<>();
            for(Dish d : menu){
                if(d.getCalories() < 400){
                    lowCaloricDishes.add(d);
                }
            }
    
            Collections.sort(lowCaloricDishes, new Comparator<Dish>() {
                @Override
                public int compare(Dish o1, Dish o2) {
                    return Integer.compare(o1.getCalories(),o2.getCalories());
                }
            });
    
            List<String> lowCaloricDishesName = new ArrayList<>();
            for(Dish d : lowCaloricDishes){
                lowCaloricDishesName.add(d.getName());
            }
    //java 8
            List<String> lowCalorDishesNames = menu.stream()
                    .filter(s->s.getCalories() < 400)
                    .sorted(Comparator.comparing(Dish::getCalories))
                    .map(Dish::getName)
                    .collect(toList());

    通过使用stream()流来完成这个功能,代码变的更简洁了,使用stream你可以把几个基础的操作连接起来,来表达复杂的数据处理流水线(在filter后面接上sorted、map和collect操作)。

    只要将.stream()更换为parallelStream()就可以完成并行操作,并行会在后面将。

    流简介

      Java 8中的集合支持了一个新的stream方法,返回一个流。后面你会看到还有很多其他的方法可以得到流。一个简短的定义就是“从支持数据处理操作的源生成的元素序列”。

    元素序列:就像集合一样,流也提供了一个接口,可以访问特定元素类型的一组有序值。但流的目的在于表达计算,集合讲的是数据。

    源:流会使用一个提供数据的源,如集合、数组或输入/输出资源。从有序集合生成流时会保留原有的顺序。

    数据处理操作:流的数据处理功能支持类似于数据库的操作,以及函数式编程语言中的常用操作(如filter、map、reduce、find、match、sort等),流可以顺序执行也可以并行执行。

    流还有两个重要的特点:

      流水线:很多流操作本身会返回一个流,这样多个操作就可以连接起来,形成一个大的流水线。

      内部迭代:与使用迭代器显示迭代集合不同,流的迭代操作是在背后进行的。

    代码解析:

      menu.stream() :从menu源获得流

      .filter(s->s.getCalories() < 400) : 过滤复合lambda表达式的数据

      .map(Dish::getName) : 获取菜名

      .limit(3) : 只取前三个

      .collect(toList()); : 将结果保存在另一个List中

    结果:[拔丝地瓜, 水煮鱼, 水煮菠菜]

    流与集合

      集合是一个内存中的数据结构,它包含数据结构中目前所有的值-集合中的每个元素都得先算出来才能添加到集合中,类似于存在DVD中的电影,从DVD上读出所有信息,

      流是在概念上固定的数据结构(你不能添加或删除 集合可以),气元素是按需计算的,流就像是一个延迟创建的集合:只有在消费者需要的时候才会计算值,类似于在互联网上看电影。

    只遍历一次 

      流和迭代器类似,只能遍历一次。遍历完之后,我们就说这个流已经被消费掉了。所以可以从原始数据源那里再获得一个新的流重新遍历一遍。

    外部迭代与内部迭代

      使用Collection接口需要用户去做迭代如foreach,这称为外部迭代。使用Streams库则是内部迭代-它帮你把迭代做了,还把得到的流值存在了某个地方,你只要给出一个函数即可。

            List<String> lowCaloricDishesName = new ArrayList<>();
            for (Dish d : lowCaloricDishes) {
                lowCaloricDishesName.add(d.getName());
            }
            //for-each 内部其实是Iterator
            Iterator<Dish> iterator = menu.iterator();
            while(iterator.hasNext()){
                Dish d = iterator.next();
                lowCaloricDishesName.add(d.getName());
            }
    //没有迭代       
    List<String> lowCalorDishesNames = menu.stream()
          .map(Dish::getName) //提取菜名
          .collect(toList());

    内部迭代与外部迭代的区别:

      外部迭代一个集合时,显示的获取每个项目再加以处理。

         (如:你:大儿子我们现在把地上的玩具都收起来吧,还有玩具吗?

                女儿:“有,球”,

        你:“好,把球放框里吧,还有吗?”

        女儿:“有,还有积木”

        你:“好,还有吗?”

        女儿:“没了,没有了”

        你:“ok,收好了”)

      而内部迭代时,项目可以透明地并行处理,或者用更优化的顺序进行处理。

       (如:你只需要说:“大儿子把地上所有的玩具都放进盒子里”就好了。 内部迭代的好处:第一、你大儿子可以左手拿一个球,右手拿一个积木,第二、你大儿子可以选择先拿离盒子比较近的东西,然后再拿别的。)

    流操作

      java.util.stream.Stream中的stream借口定义了许多操作。他们可以分为两大类:中间操作和终端操作。

      中间操作:

        如filter或sorted等中间操作会返回另一个流,这让多个操作可以连接起来形成一个查询,除非流水线上触发一个终端操作,否则中间操作不会执行任何处理。

      终端操作:

        如count,foreach终端操作会从流的流水线生成结果,其结果是任何不适流的值,比如List、Integer,甚至void。例如,下面的流水线中forEach是一个返回void的终端操作。

    menu.stream().forEach(System.out::println);

    获取所有热量高于300的菜去掉重复值的数量

    long count = menu.stream().filter(s->s.getCalories()>300).distinct().count();

    使用流

      使用流一般包括三件事:一个数据源(执行查询)、一个中间操作连(形成一条流水线)、一个终端操作(执行流水线,并生成结果)。

    到目前为止见过的流的 中间操作有:

      filter:用于过滤集合 Lambda使用的函数式接口是Predicate<T>  函数描述符是T -> boolean

      map:用于映射到一个新的类型 函数式接口是Function<T,R> 函数描述符是 T -> R

      limit: 用于截断流 获取前几个 

      sorted:用于排序流 函数式接口是Comparator<T> 函数描述符是 (T,T) -> int

      distinct: 用户去重

    终端操作有:

      forEach: 消费流中的每个元素并对其应用lambda表达式

      count: 返回流中元素的个数,long

      collect: 把流规约成一个集合,比如List、Map、Integer等

    小结:

      1.流是从支持数据处理操作的源生成的一系列元素

      2.流利用内部迭代:迭代通过filter、map、sorted等操作被抽象掉了

      3.流操作有两类:中间操作和终端操作

      4.filter和map等中间操作会返回一个流,并可以连接一起,可以用他们来设置一条流水线,但不会生成任何结果

      5.forEach和count等终端操作会返回一个非流的值,并处理流水线以返回结果

      6.流中的元素是按需计算的

  • 相关阅读:
    css3
    css3
    npm 安装包无法继续下载? 卡住
    tcp/ip协议中的SYN, ACK的数值变化
    【转】6 Reasons Why JavaScript’s Async/Await Blows Promises Away (Tutorial)
    ES6 中 Symbol.split的用法
    Why does Typescript use the keyword “export” to make classes and interfaces public?
    es6中的import,export浏览器已经支持
    Understanding the JavaScript Engine—— two phase
    【转】js-ES6学习笔记-Symbol
  • 原文地址:https://www.cnblogs.com/baidawei/p/9274625.html
Copyright © 2020-2023  润新知