Java 8 (3) Stream 流 - 简介

什么是流?java

  流是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()就能够完成并行操做,并行会在后面将。编程语言

 

流简介ide

  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,收好了”)

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

   (如:你只须要说:“大儿子把地上全部的玩具都放进盒子里”就行了。 内部迭代的好处:第1、你大儿子能够左手拿一个球,右手拿一个积木,第2、你大儿子能够选择先拿离盒子比较近的东西,而后再拿别的。)

 

流操做

  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.流中的元素是按需计算的

相关文章
相关标签/搜索