从迭代到流的操作

  • 流表面上看起来和集合很类似,都可以让我们转换和获取数据,但是它们之间存在着显著的差异

    • 流并不存储其元素,这些元素可能存储在底层的集合中,或者是按需生成的
    • 流的操作不会修改其数据源
    • 流的操作是尽可能性执行的,这意味着直至需要其结果时,操作才会执行
  • 工作流的典型流程
    • 创建一个流
    • 指定将初始流转换为其他流的中间操作,可能包含多个步骤
    • 应用终止操作,从而产生结果。这个操作会强制执行之前的惰性操作,从此之后,这个流就再也不能用了

流的创建

  • Collection接口的stream()方法:可以将任何一个集合转化成流
  • Stream.of()
    • 作用:可以将数组转化成流
    • 参数:具有可变长参数,所以可以使用其构建具有任意数量引元的流
  • Arrays.stream(array, from, to):可以从数组中于 from 和 to (不包括) 的元素中创建一个流
  • Stream.empty():创建不包含任何元素的流
  • Stream.generate():会接受一个不包含任何引元的函数 (从技术上讲,是一个 Supplier <T> 接口的对象)。无论何时,只要需要一个流类型的值,该函数就会被调用以产生一个这样的值
  • Stream.iterate():接受一个 “种子” 值,以及一个函数 (从技术上讲,是一个 UnaryOperation <T>),并且会反复地将该函数应用到之前的结果上
// Collection接口
List<String> words = new ArrayList<String>();
Stream<String> streamWords = words.stream(); // Stream.of()
String[] words = {"123", "456", "789"};
Stream<String> streamWords = Stream.of(words); // Array.stream()
Stream<String> streamWords = Array.stream(words, 0, 3); // Stream.generate()
// 产生一个随机数流
Stream<Double> randomDouble = Stream.generate(Math::random); // Stream.iterate()
// 产生一个 0,1,2,... 无限序列
Stream<BigInteger> integers = Stream.iterate(BigInteger.ZERO, n -> n.add(BigInteger>ONE));

filter、map 和 flatMap 方法

  • filter()方法

    • 作用:其会转换产生一个流,其中的元素与某种条件相匹配
    • 参数:filter()的引元是Predicate<T>,即从 T 到 boolean 的函数
  • map()方法:将传入的函数应用到每个元素上从而产生新的流
  • flatMap()方法:假设我们有一个泛型 G,以及将某种类型T转换为 G<U> 的函数 f 和将类型 U 转换为 G<V> 的函数 g,我们可以通过使用 flatMap 来组合它们,即首先应用 f,然后应用 g
// 使用 filter() 获取大于12个字母的单词
Stream<String> bigWords = words.stream().filter(w -> w.length() > 12); // 使用 map() 将单词全部转为小写
Stream<String> lowerCaseWords = words.stream().map(String::toLowerCase); // 使用 flatMap() 将一个字流中流摊平为一个流
// 获得形如 [["y", "o", "u", "r"],["a", "r", "e"]] 这样的流中流
Stream<Stream<String>> result = words.stream().map(w -> letters(w));
// flatMap() 可将其摊平,获得形如 ["y", "o", "u", "r", "a", "r", "e"] 这样的
Stream<String> flatResult = words.stream().flatMap(w -> letters(w));

抽取子流和连接流

  • limit(n):将原流的前$n$个元素组成一个新的流返回
  • skip(n):返回丢弃原流的前$n$个元素形成的新流
  • Stream.concat()
    • 作用:将两个流拼接起来
    • 注意:若第一个流是无限的,则第二个流将永远得不到处理

其他的转换流

  • distinct():返回一个由原流中剔除重复元素后所有的元素组成的新流
  • sort()
    • 直接对 Comparable 元素的流进行排序操作
    • 接受一个 Comparator
  • peek():其元素与原流相同,但是每回获取一个元素时都会调用作为其参数的函数
ArrayList<String> words;  // "ccc", "a", "bb"

// sort() 直接对 Comparable 元素的流进行排序操作
words.stream().sort(); // "a", "bb", "ccc" // sort() 接受一个 Comparator 作为参数
words.stream().sorted(Comparator.comparing(String::length).reverse()); // "ccc", "bb", "a"

简单约简

  • 约简是一种终结操作,它们会将流约简为可以在程序中使用的非流值
  • 常见简约操作
    • long count():返回流中元素个数
    • boolean anyMatch(Predicate<? super T> predicate):在这个流中任意元素匹配给定断言时返回 true
    • boolean allMatch(Predicate<? super T> predicate):在这个流中所有元素匹配给定断言时返回 true
    • boolean noneMatch(Predicate<? super T> predicate):在这个流中没有任何元素匹配给定断言时返回 true
    • Optional<T> max(Comparator<? super T> comparator):产生这个流的最大元素,使用由给定比较器定义的排序规则,如果这个流为空,会产生一个空的 Optional 对象
    • Optional<T> min(Comparator<? super T> comparator):产生这个流的最小元素,使用由给定比较器定义的排序规则,如果这个流为空,会产生一个空的 Optional 对象
    • Optional<T> findFirst():产生这个流的第一个元素,如果这个流为空,会产生一个空的 Optional 对象
    • Optional<T> findAny():产生这个流的任意一个元素,如果这个流为空,会产生一个空的 Optional 对象

Optional 类型

  • 定义

    • Optional<T>对象是一种包装器对象,要么包装了类型 T 的对象,要么没有包装任何对象
    • 对于上述的第一种情况,我们成为这种值是存在的
  • 作用:Optional<T>类型被当作一种更安全的方式,用来替代类型 T 的引用,这种引用要么引用某个对象,要么为 null

如何使用 Optional 值

  • 有效使用 Optional 值的关键:它在值不存在的情况下会产生一个可替代物,而只有在值存在的情况下才会使用这个值
  • 常用使用方法
    • T orElse(T other):产生这个 Optional 的值,或者在该 Optional 为空时,产生 other
    • T orElseGet(Supplier<? extends T> other):产生这个 Optional 的值,或者在该为空时,产生调用 other 的结果
    • <X extends Throwable> orElseThrow(Supplier<? extends X> exceptionSupplier):产生这个 Optional 的值,或者在该 Optional 为空时,抛出调用 exceptionSupplier 的结果
    • void ifPresent(Consumer<? extneds T> consumer):如果该 Optional 不为空,那么就将它的值传递给 consumer
    • <U> Optional<U> map(Function<? super T, ? extneds U> mapper):只要这个 Optional 不为空且结果不为 null,将产生该 Optional 的值传递给 mapper 后的结果,否则产生一个空 Optional
// ifPresent():如果 optionalValue 可选值存在,则将其加入到 res 中
optionalValue.ifPresent(v -> res.add(v)); // map()
// added 具有三种值之一:在 optionalValue 存在的情况下包装在 Optional 中的 true 或 false,以及在 optionalValue 不存在的情况下的空 Optional
Optional<Boolean> added = optionalValue.map(res::add);

不适合使用 Optional 值的方式

  • get():会在 Optional 值存在的情况下获得其中包装的元素,或者在不存在的情况下抛出一个 NoSuchElementException 对象
  • isPresent():如果 Optional 不为空,则返回 true
  • 注意:使用上述这两种方法并不比直接使用包装在其中的元素简便或者安全

创建 Optional 值

  • Optional.of(value):创建一个 Optional 值;如果 value 为 null,其会抛出一个 NullPointerException 对象
  • Optional.ofNullable(value):创建一个 Optional 值;如果 value 为 null,其会产生一个空 Optional
  • Optional.empty():产生一个空 Optional

使用 flatMap 来构建 Optional 的值

  • 使用场景:假设你有一个可以产生 Optional<T> 对象的方法 f,并且目标类型具有一个可以产生 Optional<T> 对象的方法 g。如果需要将两种方法结合起来,即将一种的结果作为另一种的参数,则需要使用 flatMap 才能正常结合
  • 返回值:如果 s.f() 的值存在,那么 g 就可以应用到它上面,否则就会返回一个空 Optional

收集结果

  • iterator():产生可用于访问元素的旧式风格迭代器
  • forEach()
    • 作用:将某个函数应用于所有元素
    • 注意:当应用在并行流上时,forEach 方法会以任意顺序遍历各个元素
  • forEachOrdered():按照流中的顺序遍历各个元素,但是这个方法会丧失并行处理的部分甚至全部
  • toArray()
    • 因为无法在运行时创建泛型数组,所以其会返回个 Object[]
    • 如果想要让数组具有正确的类型,可以将其传递到数组构造器中
  • collect():其接受一个 Collector 接口的实例,可以将流中的元素收集到另一个目标中。Collectors 类提供了大量用于生成公共收集器的工厂方法
    • 收集到列表中:stream.collect(Collectors.toList())
    • 收集到集合中:stream.collect(Collectors.toSet())
    • 收集到特定的数据结构中:stream.collect(Collectors.toCollection(CertainDataStructure::new))
    • 通过连接操作收集流中所有字符串:stream.collect(Collectors.joining(str)),其中 str 是分隔符,不是必要的参数
    • 流中含有除字符串以外的对象:stream.map(Object::toString).collect(Collectors.joining())
  • Collectors.summarizingInt\Long\Double:如果想要将流的结果约简为总和、平均值、最大值或最小值,可以使用 summarizingInt\Long\Double 方法中的某一个,这些方法会接受一个将流对象映射为数据的函数同时,这些方法会产生类型为 Int\Long\DoubleSummaryStatistics 的结果,同时计算总和、数量、平均值、最小值和最大值
// summarizingInt
IntSummaryStatistics statistics = stream.collect(Collectors.summarizingInt(String::length));
statistics.getSum();
statistics.getCount();
statistics.getAverage();
statistics.getMax();
statistics.getMin();

收集到映射表中

  • 方法:Collectors.toMap()
  • 实用方法
// 两个参数:用来产生映射表的键和值
// 如果多个元素具有相同的键,收集器会抛出一个 IllegalStateexception 对象
Map<Integer, String> idToName = stream.collect(Collectors.toMap(Person::getId, Person::getName));
Map<Integer, String> idToPerson = stream.collect(Collectors.toMap(Person::getId, Function.identity())); // 第三个参数:该函数会针对给定的已有值和新值来解决冲突并确定键对应的值
Map<Integer, String> idToPerson = stream.collect(Collectors.toMap(Person::getId, Function.identity(),
(existValue, newValue) -> existValue)); // 第三个参数:将构造器作为参数传入
Map<Integer, String> idToPerson = stream.collect(Collectors.toMap(Person::getId, Function.identity(),
(existValue, newValue) -> existValue),TreeMap::new);

群组和分区

  • Collectors.groupingBy():将具有相同的值群聚成组
  • Collectors.partitioningBy():当分类函数是返回 boolean 的函数时,使用这个函数更加高效,其会产生一个键为 true/false 的映射表
// 将具有相同 id 的 Person 对象分为一类
Map<Integer, List<Person>> idToManyNames = stream.collect(Collectors.groupingBy(Person::getId)); // 分类函数为 boolean 函数时
Map<Boolean, List<Person>> idEqualsCertainId = stream.collect(Collectors.partitioningBy(
person -> person.id == 1));

下游收集器

  • 作用:如果要以某些方式来处理由 groupBy 得到的列表,就需要提供一个 “下游收集器”
  • 使用方法:将方法作为 groupBy 的第二个参数
  • 常用下游收集器
    • Collectors.toSet():获得集合而不是列表
    • Collectors.counting():产生收集到的元素的个数
    • Collectors.summing():接受一个函数作为引元,将该函数应用到下游元素中,并产生它们的和
    • Collectors.maxBy():接受一个比较器,并产生下游元素中的最大值
    • Collectors.minBy():接受一个比较器,并产生下游元素中的最小值
    • Collectors.mapping():产生将函数应用到下游结果上的收集器,并将函数值传递给另一个收集器
    • Collectors.summarizingInt\Long\Double():获取 Int\Long\DoubleSummaryStatistics
// mapping()
// 获取一个州内具有最长名字的城市的名字
Map<String, Optional<String>> longestCityInState = cities.collect(
groupingBy(City::getState,
mapping(City::getName,
maxBy(Comparator.comparing()))));

约简操作

  • 方法:reduce()
  • 作用:从流中计算某个值的通用机制
  • 几种使用方法
    • 一个参数

      • 接受一个二元函数,并从前两个元素开始持续应用它
      • 这个操作必须是可结合的
    • 两个参数:第一个参数为这个操作的幺元,第二个参数为上述的二元函数
    • 三个参数:第一个参数为这个操作的幺元;第二个参数为上述的二元函数;第三个参数为一个结合函数,用于结合并行计算时产生的多个结果
// 三个参数
// 统计流中单词的长度
int totalLength = stream.reduce(0,
(total, word) -> total + word.length(),
(total1, total2) -> total1 + total2);

基本类型流

  • 三种基本类型流:IntStream、LongStream、DoubleStream
  • 创建基本类型流
    • Int\Long|DoubleStream.of()
    • Arrays.stream()
  • 生成步长为$1$的整数流
    • Int\LongStream.range(a, b):不包括 b
    • Int\LongStream.rangeClosed(a, b):包括 b
  • CharSequence 接口中的两种方法
    • codePoints:生成由字符的 Unicode 码构成的 IntStream
    • chars:生成由 UTF-16 编码机制的码元构成的 IntStream
  • 对象流转换成基本类型流:mapToInt\Long\Double()
  • 装箱成对象流:boxed()
  • 基本类型流与对象流在方法上的差异
    • toArray 方法会返回基本类型数组
    • 产生可选结果的方法会返回一个 OptionalInt、 OptionalLong 或 OptionaIDouble,这些类与 Optional 类似,但是具有 getAsInt、getAsLong 和 getAsDouble 方法,而不是 get 方法
    • 具有返回总和、平均值、最大值和最小值的 sum、average、max 和 min 方法,对象流没有定义这些方法
    • summaryStatistics 方法会产生一个类型为 IntSummaryStatistics、LongSummaryStatistics 或 DoubleSummaryStatistics 的对象

并行流

  • 获取并行流

    • parallelStream():从集合中直接获取一个并行流
    • parallel():将一个流转化为并行流
  • 放弃排序需求:当放弃排序需求时,有些操作可以更有效的并行化,通过再流上调用 unordered() 方法,就可以明确的表示对排序不感兴趣
  • 注意
    • 不要修改在执行某项流操作后会将元素返回到流中的集合 (即使这种修改是线程安全的)
    • 数据应该在内存中,必须等到数据到达是非常低效的
    • 流应该可以被高效地分成若干个子部分,由数组或平衡二叉树支撑的流都可以工作得很好,但是 Stream. iterate 返回的结果不行
    • 流操作的工作量应该具有较大的规模,如果总工作负载并不是很大,那么搭建并行计算时所付出的代价就没有什么意义
    • 传递给并行流操作的函数不应该被堵塞,并行流使用 fork-join 池来操作流的各个部分,如果多个流操作被阻塞,那么池可能就无法做任何事情了

第01章-Java SE8的流库的更多相关文章

  1. Java SE 8 流库

    1. 流的作用 通过使用流,说明想要完成什么任务,而不是说明如何去实现它,将操作的调度留给具体实现去解决: 实例:假如我们想要计算某个属性的平均值,那么我们就可以指定数据源和属性,然后,流库就可以对计 ...

  2. Java SE 8 流库(一)

    1. 流的作用 通过使用流,说明想要完成什么任务,而不是说明如何去实现它,将操作的调度留给具体实现去解决: 实例:假如我们想要计算某个属性的平均值,那么我们就可以指定数据源和属性,然后,流库就可以对计 ...

  3. Java SE 8 流库(二)

    1.3. filter,map,flatMAP方法 流的转换会产生一个新流,它的元素派生出自另一个流中的元素: Stream<T> filter(Predicate<? super ...

  4. Java SE 8 流库(四)

    1.8. 收集数据 <R,A> R collect(Collector<? super T,A,R> collector)   使用给定的收集器来收集当前流中的元素 void ...

  5. Java SE 8 流库(三)

    1.7. Optional类型 容器对象,可能包含或不包含非空值.如果存在一个值,isPresent()将返回true,get()将返回值.还提供了依赖于包含值是否存在的附加方法,如orElse()( ...

  6. Java高级特性1_流库_初体验

    Java高级特性流库_初体验 面对结果编程 在编程里, 有两种编程方式, 一种是面对过程编程, 一种是面对结果编程. 两者区别如下 面向过程编程 面向过程编程需要编程程序让程序依次执行得到自己想要的结 ...

  7. Gradle 1.12用户指南翻译——第46章. Java 库发布插件

    本文由CSDN博客貌似掉线翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...

  8. Java SE 8 的流库学习笔记

    前言:流提供了一种让我们可以在比集合更高的概念级别上指定计算的数据视图.如: //使用foreach迭代 long count = 0; for (String w : words) { if (w. ...

  9. 第15章-输入/输出 --- 理解Java的IO流

    (一)理解Java的IO流 JAVA的IO流是实现输入/输出的基础,它可以方便地实现数据的输入/输出操作,在Java中把不同的输入/输出(键盘.文件.网络连接等)抽象表述为"流"( ...

随机推荐

  1. 13. 从0学ARM-Cortex-A9 RTC裸机程序编写

    一.RTC RTC(Real-Time Clock) 实时时钟. RTC是集成电路,通常称为时钟芯片.在一个嵌入式系统中,通常采用RTC来提供可靠的系统时间,包括时分秒和年月日等,而且要求在系统处于关 ...

  2. k8s-2-集成apollo配置中心

    主题: 在k8s中集成Apollo配置中心 架构图 一.配置中心概述 配置的几种方式 本课讲得是基于配置中心数据库实现 配置管理的现状 常见的配置中心 主讲:k8s configmap,apollo ...

  3. npm publish & 403 Forbidden

    npm publish & 403 Forbidden 403 Forbidden - PUT https://registry.npmjs.org/ https://www.npmjs.co ...

  4. CustomEvent & Event

    CustomEvent & Event js 自定义事件 const event = new CustomEvent(typeArg, customEventInit); // add an ...

  5. React Slingshot

    React Slingshot React 弹弓 https://github.com/coryhouse/react-slingshot https://decoupledkit-react.rea ...

  6. HTTPS in depth

    HTTPS in depth HTTPS Hypertext Transfer Protocol Secure How does HTTPS work? https://www.cloudflare. ...

  7. 从长度为 M 的无序数组中,找出N个最小的数

    从长度为 M 的无序数组中,找出 N个最小的数 在一组长度为 n 的无序的数组中,取最小的 m个数(m < n), 要求时间复杂度 O(m * n) 网易有道面试题 const minTopK ...

  8. Open Collective

    Open Collective Open Collective is an online funding platform for open and transparent communities. ...

  9. Electron All In One

    Electron All In One desktop app https://www.electronjs.org/docs/api/browser-window BrowserWindow 创建和 ...

  10. 时间轴 timeline

    时间轴 timeline https://www.helloweba.net/javascript/285.html https://www.helloweba.net/demo/v_timeline ...