文章目录

  • 前言
  • 流的定义
  • 流简介
  • 流与集合
  • 只能遍历一次
  • 流操作
  • 使用流
  • 总结


前言

Java 8引入了流操作,可以对数据进行命令式操作(与数据库类似的操作),使得代码可读性大大增强。同时,流的引入也减少了开发过程中的代码量,是对开发人员的福利。


流的定义

流允许用户以声明性方式处理数据集合,目前阶段可视为遍历数据集的高级迭代器。此外,流可以透明地并行处理。

// TODO: 筛选菜单中低热量的菜肴
// Java 7
 List<Dish> lowCaloricDishes = new ArrayList<>();
 for (Dish d : menu) {
     if (d.getCalories() < 400) {
         lowCaloricDishes.add(d);
     }
 }
Collections.sort(lowCaloricDishes, new Comparator<Dish>() {
    public int compare(Dish d1, Dish d2) {
        return Integer.compare(d1.getCalories(), d2.getCalories());
    }
});
List<String> lowCaloricDishesName = new ArrayList<>();
for (Dish d : lowCaloricDishes) {
    lowCaloricDishesName.add(d.getName());
}
// Java 8 Stream
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.toList;
List<String> lowCaloricDishesName = menu.stream()
    // 选出400卡以下的菜
    .filter(d -> d.getCalories() < 400)
    // 按照卡路里排序
    .sorted(comparing(Dish::getCalories))
    // 提取菜肴名称
    .map(Dish::getName)
    // 将所有名称保存在List中
    .collect(toList());
// 为了利用多核架构并行执行,只需要将 stream() 改为 parallelStream()

新方法的好处:

  • 代码以声明性方式写的:说明想要完成什么而不是说明如何实现一个操作
  • 可以将几个基础操作链接起来,来表达复杂的数据处理流水线

流简介

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

  • 元素序列——和集合一样,流提供了一个接口,可以访问特定元素类型的一组有序值
  • 源——流会使用一个提供数据的源头
  • 数据处理操作——类似于数据库的操作,以及函数式编程语言中的常用操作

此外,流操作的两个特点:

  • 流水线——流操作本身会返回一个流,多个操作可以链接起来
  • 内部迭代

流与集合

粗略角度

差异:什么时候进行计算

  • 集合:一个内存中的数据结构,包含数据结构中目前所有的值——集合中每个元素都得先算出来才能添加到集合中
  • 流:概念上固定的数据结构(无法添加或删除元素),其元素是按需计算的。满足生产者-消费者的关系

只能遍历一次

流只能遍历一次,遍历完之后,流被销毁

List<String> title = Arrays.asList("Java8", "In", "Action");
Stream<String> s = title.stream();
// 打印标题中的每个单词
s.forEach(System.out::println);
// java.lang.IllegalStateException:流已被操作或关闭
s.forEach(System.out::println);

流操作

  • 中间操作:可连接起来的流操作,例如 filter、map、limit
  • 终端操作:关闭流的操作,例如 collect

使用流

使用流包括三件事:

  • 一个数据源来执行一个查询;
  • 一个中间操作链,形成 一条流的流水线;
  • 一个终端操作,执行流水线,并能生成结果。

总结

本文介绍了流的概念,后续梳理了流与集合的差异,以及流的操作和注意事项。希望在后续开发中积极思考何时用流,如何用流,提升程序效率。