Java8 实战读书笔记(4~6章)

第4章 引入流

流是什么?

是Java API 的新成员,它允许你以声明性方式处理数据集合。你可以把它们看作是遍历数据集的高级迭代器。

优点:

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

写出代码的特性:

  • 声明性
  • 可复合
  • 可并行

流简介

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

对以上定义中单个词进行剖析:

  • 元素序列: 就像集合一样,流也提供了一个接口,可以访问特定元素类型的一组有序值。集合讲的是数据,流讲的是计算
  • 流会使用一个提供数据的源(也就是数据的来源),从有序集合生成流时会保留原有的顺序。由列表生成的流,其元素顺序与列表保持一致。
  • 数据处理操作 流的数据处理功能支持类似于数据库的操作。流操作可以顺序执行也可以并行执行。
    此外流操作还有两个重要的特点:
  • 流水线 很多流操作本身会返回一个流,这样多个操作就可以链接起来,形成一个大的流水线。
  • 内部迭代 与使用迭代器显式迭代的集合不同,流的迭代操作是在背后进行的。

部分操作:

  • filter 接受lambda,从流中排序某些元素。
  • map 接受一个lambda,将元素转换成其他形式或提取信息。
  • limit 截断流,使其元素不超过给定元素。
  • collect 将流转换为其他形式。用的最多的就是toList。

流与集合

差异性

  • 执行时段 集合是,元素都得先算出来才能为集合的一部分,而流是按需计算的,像是一个延时创建的集合。
只能遍历一次

流只能消费一次

外部迭代与内部迭代

使用Collection接口需要用户去做迭代(比如for-each),这称为外部迭代。相反Stream库使用内部迭代,它帮你把迭代做了,还把得到的流值存在了某个地方,你只要给出一个函数说要干什么就可以了。

流操作

流操作分为两大类:

  • 中间操作
  • 终端操作
中间操作

中间操作会由一个流返回另外一个流,除非流水线上触发一个终端操作否则中间操作不会执行任何操作。

终端操作

终端操作会从流的流水线生成结果,其结果是任何不为流的值。

使用流

总而言之,流的使用一般包括三件事:

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

注意上面的终端操作,一般在工作过程中我们对一个集合排序,写代码时会发现,有List.sort(),和List.stream.sorted(),如下所示:
示例:

    @Test
    public void test() {
        List<TestBean2> list = Arrays.asList(new TestBean2(5, "e"), new TestBean2(2, "d"), new TestBean2(2, "c"), new TestBean2(4, "b"), new TestBean2(1, "a"));
        // list排序会有种误解,list.sort()和list.stream.sorted()有什么区别,后者是用的流,如果没有终端操作(collect())这个list不会发生任何变化。
        List<TestBean2> list1 = list.stream().sorted(Comparator.comparing(TestBean2::getAge).thenComparing(TestBean2::getName)).collect(Collectors.toList());
        list1.stream().map(TestBean2::toString).forEach(System.out::println);
    }

部分中间操作:

操作 类型 返回类型 操作参数 函数描述符
filter 中间 Stream Predicate T -> boolean
map 中间 Stream Function<T,R> T -> R
limit 中间 Stream
sorted 中间 Stream Compartor (T,T) -> int
distinct 中间 Stream Stream

部分终端操作:

操作 类型 目的
forEach 终端 消费流中的每个元素并对其应用Lambda。这一操作返回void
count 终端 返回流中元素的个数。这一操作返回long
collect 终端 把流归约成一个集合,比如List、Map甚至是Integer。

第五章 使用流

筛选与切片

  • 用谓词筛选 利用filter方法。参数为Predicate 类型谓词。
  • 筛选各异的元素 利用distinct方法。起到去重的作用。
  • 截短流 利用limit方法,限制其长度。
  • 跳过元素 利用skip方法,跳过前指定个数元素。

映射

对流中每一个元素用函数

流支持map方法,它会接受一个函数作为参数。这个函数不限于获取序列元素的属性值,还可以实现将序列元素转换成另外一个对象,也就是类型转换。

流的扁平化

flatMap方法让你把一个流中的每个值都换成另一个流,然后把所有的流连接成为一个流。

查找和匹配

匹配的几种方法:

  • allMatch
  • anyMatch
  • noneMatch
  • findFirst
  • findAny

短路求值:有些操作不需要处理整个流就能得到结果。
查找的几种方法:

  • findAny
  • findFirst
    何时使用findFirst和findAny
    如果你对查找的结果没有顺序要求,那么可以通过findAny方法使用并行流提高查找效率。
    Optional:
    Optional 类是一个容器类,代表一个值存在或者不存在。

有以下四个方法:

  • isPresent() 将在Optional包含值的时候返回true,否则返回false。
  • ifPresent(Consumer block) 会在值存在的时候执行给定代码块。
  • T get() 会在值存在时返回值,否则抛出NoSuchElement异常。
  • T orElse(T other) 会在值存在时返回值,否则返回一个默认值。参数属性赋值时类似于三元运算。

归约

关键词:reduce
归约操作:将流归约成一个值。

元素求和

示例:

    //有初始值
    int sum = numbers.stream().reduce(0,(a,b) -> a + b);
    //无初始值
    int sum = numbers.stream().reduce((a,b) -> a + b);
最大值最小值

示例:

    // 求最大值
    Optional<Integer> max = numbers.stream().reduce(Integer::max);
    // 求最小值
    Optional<Integer> min = numbers.stream().reduce(Integer::min);
流操作:无状态和有状态
  • 无状态:没有内部状态,需要和有状态进行比较理解。
  • 有状态:是可能需要一个内部状态来积累结果,比如在做reduce、sum、max等操作每内部迭代一次,需要将上次迭代的结果保存下来,留作下次迭代使用,这个保存就需要内部状态来保存,所以叫有状态,以上三个方法在一次迭代得到的结果是一个int或double,所以这个内部状态有界的,以为需要存储的值就一个,相反诸如sort或distinct等操作,进行排序或者去重的时候需要所有元素都放到内部状态中一起进行排序进行去重,这个要求是无界的,因为不知道这个流有多少个元素。

数值流

原始类型流特化

Java 8引入了三个原始类型特化流接口来避免了暗含的装箱成本:

  • IntStream
  • DoubleStream
  • LongStream

映射到数值流

  • mapToInt:会接收Integer类型返回一个IntStream而不是Streams ,IntStream有sum、max、min、average等方法。
  • mapToDouble
  • mapToLong

转换回对象流

  • Stream stream = intStream.boxed();
  • Stream stream = doubleStream.boxed();
  • Stream stream = longSteam.boxed();

默认值OptionalInt:

  • OptionalInt
  • OptionalDouble
  • OptionalLong
    以上方法是在做数值流取值时,不知道该值是否存在而设的一个接收容器。有方法orElse(),当所取得值不存在时设置一个默认值,如下示例:
    // mapToInt 是转换成了对象流,并在这个对象流中取最大值(max()),如果为空则设置一个默认值为1(orElse).
    int max = menu.stream().mapToInt(Dish::getCalories).max().orElse(1);
数值范围

Java 8 引入了可以用于IntStream和LongStream可以生成一定范围的所有数字的静态方法:

  • range(start,end) 结果值不包含(end)
  • rangeClosed(start,end) 结果值包含结束值(end)
    以上方法可以链接filter方法用于筛选特定值(奇数、偶数等)。

示例:

    IntStream evenNumbers = IntStream.rangeClosed(1,100).filter(n -> n % 2 == 0);

构建流

由值创建流

可以使用Stream.of,它可以接受任意数量的参数。

示例:

    // 得到一个字符串流
    Stream<String> stream = Stream.of("Java 8","Lambda","In","Action");
    // 得到一个空流
    Stream<String> emptyStream = Stream.empty();
有数组创建流

可以使用Arrays.stream,

示例:

    int[] numbers = {2,3,5,7,11,13};
    int sum = Arrays.stream(numbers).sum();
由文件生成流

Java 中用于处理文件等I/O操作的NIO API(非阻塞I/O)已更新,以便利用Stream API.
java.nio.file.Files中的很多静态方法都会返回一个流。其中一个很有用的方法是:Files.lines,它会返回一个由指定文件中的各行构成的字符串流。

有函数生成流: 创建无限流
  • Stream.iterate 迭代
  • Stream.generate 生成

第六章 用流收集数据

收集器简介

收集器用作高级归约

对流调用collect方法将对流中的元素触发一个归约操作(由Collector来参数化)。一般来说,Collector会对元素应用一个转换函数。

预定义收集器

主要提供三大功能:

  • 将流元素归约和汇总为一个值
  • 元素分组
  • 元素分区

归约和汇总

在需要将流项目重组成集合时,一般会使用收集器,但凡要把流中所有项目合并成一个结果是就可以用,这个结果是任何类型的。

查找流中的最大值和最小值

猜你喜欢

转载自www.cnblogs.com/bibibao/p/11443965.html