美文网首页
Java 8 Stream in Action

Java 8 Stream in Action

作者: 但时间也偷换概念 | 来源:发表于2019-12-01 16:01 被阅读0次

引言

本篇博客主要基于<Java 8 in Action>、<Java 8函数式编程>等书总结一下Stream流概况。

一:流是什么?

首先来看一下声明性方式处理数据集合带来的差异。

首先我们一个实体类型Dish

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@AllArgsConstructor
@Setter
@Getter
@ToString
public class Dish {
    private String name;
    private boolean vegetarian;
    private int calories;
    private Type type;

    public enum Type {MEAT, FISH, OTHER}
}

然后我们试着mock一个Dish集合,menu。我们要进行的操作是将menu中所有calories小于400的Dish name有序集合返回。

集合处理

         // java 7 in Action
        List<Dish> menu = Lists.newArrayList();
        List<Dish> lowCaloricDishes = Lists.newArrayList();
        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 = Lists.newArrayList();
        for (Dish d : lowCaloricDishes) {
            lowCaloricDishesName.add(d.getName());
        }

流处理

       // java 8 in Action
        menu.stream()
                .filter(d -> d.getCalories() < 400)
                .sorted(Comparator.comparing(Dish::getCalories))
                .map(Dish::getName)
                .collect(Collectors.toList());

并行流处理

       // java 8 in action
        menu.parallelStream()
                .filter(d -> d.getCalories() < 400)
                .sorted(Comparator.comparing(Dish::getCalories))
                .map(Dish::getName)
                .collect(Collectors.toList());

总结一下以上代码。

Java 7 in Action
  • 代码说明如何实现
  • 有"垃圾变量"lowCaloricDishes
  • 需要仔细阅读实现才能知道流水线做了哪些事情
  • 如果要并行处理,需要自己去实现多线程代码
  • 并行处理中需要自己设计与保证线程安全
  • 显示迭代,需要自己优化迭代性能
Java 8 in Action
  • 声明性代码,表达目的,而不是实现
  • 整个流水线可读性强
  • 并行操作无需自己处理
  • 代码简单
  • 可享受隐式迭代jdk平滑升级的性能优化

二:流简介

简短定义

"从支持数据处理操作的源生成的元素序列"

  • 元素序列
    就像集合一样,流也提供了一个接口,可以访问特定元素类型的一组有序值。因为集合是数据结构,所以它的主要目的是以特定的时间/空间复杂度存储和访问元素(如ArrayList与LinkedList)。但流的目的在于表达计算,比如前面的filter、sorted、map。集合讲的是数据,流讲的是计算。

  • 流会使用一个提供数据的源,如集合、数组、输入/输出资源。请注意,从有序集合生成的流会保留原有的顺序。
  • 数据处理操作
    类似于数据库的操作,以及函数式编程语言中的操作。如filter、map、reduce、find、match、sort等。流操作可以顺序执行,也可以并行执行。
  • 流水线
    很多流操作本身会返回一个流,这样多个操作就可以链接起来,形成一个大的流水线。流水线的操作可以看作对数据源进行数据库式查询。
  • 内部迭代
    与显示迭代不同,流的迭代操作是在背后进行的

三:流与集合

粗略的说:"流和集合之间的差异在于什么时候进行计算,还有遍历的方式"

  • 流只能遍历一次
    如果对同一个流使用两次,将会出现此类异常。
        Stream<Dish> stream = menu.stream();
        stream.collect(Collectors.toList());
        stream.collect(Collectors.toList());

Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:229)
    at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
    at com.zhengyu.stream.StreamAction.main(StreamAction.java:39)

  • 计算时机不同
    流可以延迟懒加载、按需使用、实时计算
    集合必须准备全部数据

  • 遍历方式
    集合属于显示遍历,需要自己处理优化&线程安全&并行
    流属于隐式遍历,Java 8需要一个类似于Collection却没有迭代器的接口,于是就有了Stream。

四:流操作

流的使用一般包括三件事
  • 一个数据源来执行一个查询
  • 一个中间操作链,形成一条流的流水线
  • 一个终端操作,执行流水线,并能生成结果
    整个过程类似于builder模式,在builder模式中有一个调用链用来设置一套配置(相当于中间操作),最后调用一个build方法生成结果(相当于终端操作)。
   // java 8 in action
        menu.stream()
                .filter(d -> d.getCalories() < 400)
                .sorted(Comparator.comparing(Dish::getCalories))
                .map(Dish::getName)
                .collect(Collectors.toList());

可以看到两类操作:

  • 中间操作
    可以链接起来的操作成为"中间操作",如filter、sorted、map、limit。
  • 终端操作
    关闭流的操作成为"终端操作",如collect、find、count。

在中间操作中,还会有短路技巧与循环合并技术,能够按需计算,减少遍历的次数。

    // java 8 in action
        menu.stream()
                .filter(d -> {
                    System.out.println("filtering" + d.getName());
                    return d.getCalories() < 400;
                })
                .sorted(Comparator.comparing(Dish::getCalories))
                .map(d -> {
                    System.out.println("mapping" + d.getName());
                    return d.getName();
                })
                .limit(3)
                .collect(Collectors.toList());

如上述代码,由于limit和短路,加上filter和map循环合并。
最终只会打印三条filtering和mapping日志。

image.png

相关文章

网友评论

      本文标题:Java 8 Stream in Action

      本文链接:https://www.haomeiwen.com/subject/hfbzwctx.html