Java8实战笔记0x01

流简介

  • 流是从支持数据处理操作的源生成的元素序列。
  • 集合的核心是数据,而流的目的在于表达计算。
  • 和迭代器类似,流只能遍历一次。
  • 使用Collection接口需要用户做迭代,称为外部迭代。而使用Stream则代替用户做了迭代,称为内部迭代。

流操作

List<String> names = menu.stream()
                         // 中间操作
                         .filter(d -> d.getCalories())
                         .map(Dist::getName)
                         .limit(3)
                         // 终端操作
                         .collect(toList());
  • 诸如filtersorted等中间操作会返回另一个流,这让多个操作可以连接起来形成一个查询。除非流水线上触发一个终端操作,否则中间操作不会执行任何处理,因为中间操作一般都可以合并起来,在终端操作时一次性全部处理,这种技术称作循环合并。
  • 终端操作会从流的流水线生成结果。其结果是任何不是流的值,比如ListInteger,甚至是void

使用流

筛选和切片

用谓词筛选

  • filter方法:接受一个谓词(返回boolean的函数)作为参数,并返回一个包括所有符合谓词的元素的流。
List<Dish> vegetarianMenu = menu.stream()
                                .filter(Dish::isVegetarian)
                                .collect(toList());

提取不同元素

  • distinct方法:无参数,返回一个元素各异的流(根据流所生成元素的hashCode()equals()方法实现)。
List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
numbers.stream()
       .filter(i -> i % 2 == 0)
       .distinct()
       .forEach(System.out::println);
/* Output:
2
4
*/

截短流

  • limit(n)方法:返回一个不超过给定长度n的流。如果流是有序的,则最多返回钱n个元素;如果流是无序的(例如Set),则返回结果不会以任何顺序排列。

跳过元素

  • skip(n)方法:返回一个去掉前n个元素的流。如果流中的元素不足n个,则返回一个空流。与limit(n)是互补操作。

映射

对流中的每个元素应用函数

  • map方法:接受一个函数作为参数,这个函数会被应用到每个元素上,并将其映射成一个新的元素(与转换的区别是,该步骤是创建一个新的版本而不是修改)。
List<String> dishName = menu.stream()
                            .map(Dish::getName)
                            .collect(toList());

流的扁平化

  • flatMap方法:传入的方法生成的流不会各自独立,而是会被合并,扁平化称为一个流。

查找和匹配

  • anyMatch检查谓词是否至少匹配一个元素;allMatch会检查谓词是否匹配所有元素,相对的noneMatch则会确保流中没有任何元素与给定的谓词匹配。这三种方法都用到了短路。
  • findAny可以返回流中的任意元素,而findFirst则返回有出现顺序的流中的第一个元素。使用时配合Optional<T>使用。此外findAny在使用并行流时限制较少,如果不关心返回哪个元素,那么应该使用findAny

归约

int sum = numbers.stream().reduce(0, (a, b) -> a + b);
  • 上面代码展示了用reduce求一组整数的和,reduce接受一个初始值和一个函数,它将使用函数反复结合每个元素,直到流被归约成一个值。
  • 使用reduce可以让内部实现得以选择并行执行操作。但是,传递给reduce的Lambda不能更改状态(如实例变量),而且操作必须满足结合律才可以按任意顺序执行。

数值流

  • IntStreamDoubleStreamLongStream分别将流中的元素特化为intdoublelong,从而避免的暗含的装箱成本。数值流包含进行常用归约的方法,如求和sum,找到最大元素max等。
  • 映射到数值流可以用mapToIntmapToDoublemapToLong,而使用boxed方法可以将数值流转回对象流(各个基本类型对应的包装类型)。
  • rangerangeClosed是可以用于IntStreamLongStream的静态方法,作用是生成范围内的数,区别是range不包含结束值而rangeClosed包含。

构建流

由值创建流

  • 使用Stream.of可以通过显式值创建一个流,可以接受任意数量的参数。而Stream.empty可以得到一个空流。
Stream<String> stream = Stream.of("Java 8", "Lambdas", "In", "Action");
Stream<String> emptyStream = Stream.empty();

由数组创建流

  • 静态方法Arrays.stream可以从数组创建一个流,接受一个数组作为参数。

由文件生成流

  • java.nio.file.Files中的很多静态方法都会返回一个流,例如Files.lines,它会返回一个由指定文件中的各行构成的字符串流。

由函数生成流——创建无限流

迭代

  • iterate方法接受一个初始值,以及一个一次应用在每个产生的新值上的Lambda。iterate操作基本上是顺序的,因为结果取决于前一次应用。该操作将生成一个无限流,没有结尾,因为值是按需计算的,可以永远计算下去。因此流和集合的一个关键的区别在于流是无界的。

生成

  • generate方法也可以生成无限流,它接受一个Supplier<T>类型的Lambda提供新的值。
Author: SinLapis
Link: http://sinlapis.github.io/2019/07/30/Java8实战笔记0x01/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.