From 30986939c390dd4f15a0087fb3c17590bfefac4a Mon Sep 17 00:00:00 2001 From: LingCoder Date: Sat, 11 Jan 2020 17:57:08 +0800 Subject: [PATCH] =?UTF-8?q?[ISSUE=20#40]=E6=A0=A1=E8=AE=A2=E6=B5=81?= =?UTF-8?q?=E5=BC=8F=E7=BC=96=E7=A8=8B=20close=20#40?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/book/13-Functional-Programming.md | 2 +- docs/book/14-Streams.md | 64 +++++++++++++------------ docs/book/Appendix-Collection-Topics.md | 2 +- 3 files changed, 35 insertions(+), 33 deletions(-) diff --git a/docs/book/13-Functional-Programming.md b/docs/book/13-Functional-Programming.md index ab7ec49..4ede66b 100644 --- a/docs/book/13-Functional-Programming.md +++ b/docs/book/13-Functional-Programming.md @@ -646,7 +646,7 @@ public class FunctionalAnnotation { 4. 如果返回值类型与参数类型一致,则是一个运算符:单个参数使用 `UnaryOperator`,两个参数使用 `BinaryOperator`。 -5. 如果接收两个参数且返回值为布尔值,则是一个谓词(Predicate)。 +5. 如果接收两个参数且返回值为布尔值,则是一个断言(Predicate)。 6. 如果接收的两个参数类型不同,则名称中有一个 `Bi`。 diff --git a/docs/book/14-Streams.md b/docs/book/14-Streams.md index 142079a..b75adc6 100644 --- a/docs/book/14-Streams.md +++ b/docs/book/14-Streams.md @@ -1683,15 +1683,15 @@ Signal(dash) ## 终端操作 -这些操作接收一个流并产生一个最终结果;它们不会向后端流提供任何东西。因此,终端操作总是你在管道中做的最后一件事情。 +以下操作将会获取流的最终结果。至此我们无法再继续往后传递流。可以说,终端操作总是我们在流管道中所做的最后一件事。 -### 转化为数组 +### 数组 - `toArray()`:将流转换成适当类型的数组。 -- `toArray(generator)`:在特殊情况下,生成器用于分配自定义的数组存储。 +- `toArray(generator)`:在特殊情况下,生成自定义类型的数组。 -这组方法在流操作产生的结果必须是数组形式时很有用。假如我们想在流里复用获取的随机数,可以将他们保存到数组中。代码示例: +当我们需要得到数组类型的数据以便于后续操作时,上面的方法就很有用。假设我们需要复用流产生的随机数时,就可以这么使用。代码示例: ```java // streams/RandInts.java @@ -1706,19 +1706,19 @@ public class RandInts { } ``` -上例将100个数值范围在 0 到 1000 之间的随机数流转换成为数组并将其存储在 `rints` 中,在每次调用 `rands()` 的时候可以重复获取相同的流。 +上例将100个数值范围在 0 到 1000 之间的随机数流转换成为数组并将其存储在 `rints` 中。这样一来,每次调用 `rands()` 的时候可以重复获取相同的整数流。 -### 应用最终操作 +### 循环 -- `forEach(Consumer)`:你已经看到过很多次 `System.out::println` 作为 **Consumer** 函数。 +- `forEach(Consumer)`常见如 `System.out::println` 作为 **Consumer** 函数。 - `forEachOrdered(Consumer)`: 保证 `forEach` 按照原始流顺序操作。 -第一种形式:显式设计为任意顺序操作元素,仅在引入 `parallel()` 操作时才有意义。在 [并发编程](24-Concurrent-Programming.md) 章节之前我们不会深入研究这个问题。这里简单介绍下 `parallel()`:可实现多处理器并行操作。实现原理为将流分割为多个(通常数目为 CPU 核心数)并在不同处理器上分别执行操作。因为我们采用的是内部迭代,而不是外部迭代,所以这是可能实现的。 +第一种形式:无序操作,仅在引入并行流时才有意义。在 [并发编程](24-Concurrent-Programming.md) 章节之前我们不会深入研究这个问题。这里简单介绍下 `parallel()`:可实现多处理器并行操作。实现原理为将流分割为多个(通常数目为 CPU 核心数)并在不同处理器上分别执行操作。因为我们采用的是内部迭代,而不是外部迭代,所以这是可能实现的。 `parallel()` 看似简单,实则棘手。更多内容将在稍后的 [并发编程](24-Concurrent-Programming.md) 章节中学习。 -下例引入了 `parallel()` 来帮助理解 `forEachOrdered(Consumer)` 的作用和使用场景。代码示例: +下例引入 `parallel()` 来帮助理解 `forEachOrdered(Consumer)` 的作用和使用场景。代码示例: ```java // streams/ForEach.java @@ -1756,7 +1756,7 @@ public class ForEach { -### 收集 +### 集合 - `collect(Collector)`:使用 **Collector** 收集流元素到结果集合中。 - `collect(Supplier, BiConsumer, BiConsumer)`:同上,第一个参数 **Supplier** 创建了一个新结果集合,第二个参数 **BiConsumer** 将下一个元素包含到结果中,第三个参数 **BiConsumer** 用于将两个值组合起来。 @@ -1798,7 +1798,7 @@ stream, streams, throws, toCollection, trim, util, void, words2] ``` -**Files.**`lines()` 打开 **Path** 并将其转换成为行流。下一行代码将匹配一个或多个非单词字符(`\\w+`)行进行分割,然后使用 **Arrays.**`stream()` 将其转化成为流,并将结果扁平映射成为单词流。使用 `matches(\\d+)` 查找并移除全数字字符串(**注意**,`words2` 是通过的)。接下来我们使用 **String.**`trim()` 去除单词两边的空白,`filter()` 过滤所有长度小于3的单词,紧接着只获取100个单词,最后将其保存到 **TreeSet** 中。 +**Files.**`lines()` 打开 **Path** 并将其转换成为行流。下一行代码将匹配一个或多个非单词字符(`\\w+`)行进行分割,然后使用 **Arrays.**`stream()` 将其转化成为流,并将结果展平映射成为单词流。使用 `matches(\\d+)` 查找并移除全数字字符串(**注意**,`words2` 是通过的)。接下来我们使用 **String.**`trim()` 去除单词两边的空白,`filter()` 过滤所有长度小于3的单词,紧接着只获取100个单词,最后将其保存到 **TreeSet** 中。 -### 组合所有流元素 +### 组合 - `reduce(BinaryOperator)`:使用 **BinaryOperator** 来组合所有流中的元素。因为流可能为空,其返回值为 **Optional**。 - `reduce(identity, BinaryOperator)`:功能同上,但是使用 **identity** 作为其组合的初始值。因此如果流为空,**identity** 就是结果。 -- `reduce(identity, BiFunction, BinaryOperator)`:这个形式更为复杂(所以我们不会介绍它),在这里被提到是因为它使用起来会更有效。通常,你可以显式地组合 `map()` 和 `reduce()` 来更简单的表达它。 +- `reduce(identity, BiFunction, BinaryOperator)`:更复杂的使用形式(暂不介绍),这里把它包含在内,因为它可以提高效率。通常,我们可以显式地组合 `map()` 和 `reduce()` 来更简单的表达它。 -如下是一个用于演示 `reduce()` 的示例: +下面来看下 `reduce` 的代码示例: ```java // streams/Reduce.java @@ -1946,20 +1946,20 @@ Frobnitz(7) Frobnitz(29) ``` -**Frobnitz** 包含了一个名为 `supply()` 的生成器;因为这个方法对于 `Supplier` 是签名兼容的,我们可以将其方法引用传递给 `Stream.generate()`(这种签名兼容性被称作结构一致性)。我们使用没有给“起始值”的 `reduce()`方法,这意味着它的返回值是 **Optional** 类型的。`Optional.ifPresent()` 只有在结果非空的时候才会调用 `Consumer` (`println` 方法可以被调用是因为 **Frobnitz** 可以通过 `toString()` 方法转换成 **String**)。 +**Frobnitz** 包含了一个名为 `supply()` 的生成器;因为这个方法对于 `Supplier` 是签名兼容的,我们可以将其方法引用传递给 `Stream.generate()`(这种签名兼容性被称作结构一致性)。无“初始值”的 `reduce()`方法返回值是 **Optional** 类型。`Optional.ifPresent()` 只有在结果非空的时候才会调用 `Consumer` (`println` 方法可以被调用是因为 **Frobnitz** 可以通过 `toString()` 方法转换成 **String**)。 Lambda 表达式中的第一个参数 `fr0` 是上一次调用 `reduce()` 的结果。而第二个参数 `fr1` 是从流传递过来的值。 -`reduce()` 中的 Lambda 表达式使用了三元表达式来获取结果,当其 size 小于 50 的时候获取 `fr0` 否则获取序列中的下一个值 `fr1`。因此你会取得第一个 size 小于 50 的 `Frobnitz`,只要找到了就这个结果就会紧紧地攥住它,即使有其他候选者出现。虽然这是一个非常奇怪的约束,但是它确实让你对 `reduce()` 有了更多的了解。 +`reduce()` 中的 Lambda 表达式使用了三元表达式来获取结果,当其长度小于 50 的时候获取 `fr0` 否则获取序列中的下一个值 `fr1`。当取得第一个长度小于 50 的 `Frobnitz`,只要得到结果就会忽略其他。这是个非常奇怪的约束, 也确实让我们对 `reduce()` 有了更多的了解。 ### 匹配 -- `allMatch(Predicate)` :如果流的每个元素根据提供的 **Predicate** 都返回 true 时,结果返回为 true。这个操作将会在第一个 false 之后短路;也就是不会在发生 false 之后继续执行计算。 -- `anyMatch(Predicate)`:如果流中的任意一个元素根据提供的 **Predicate** 返回 true 时,结果返回为 true。这个操作将会在第一个 true 之后短路;也就是不会在发生 true 之后继续执行计算。 -- `noneMatch(Predicate)`:如果流的每个元素根据提供的 **Predicate** 都返回 false 时,结果返回为 true。这个操作将会在第一个 true 之后短路;也就是不会在发生 true 之后继续执行计算。 +- `allMatch(Predicate)` :如果流的每个元素根据提供的 **Predicate** 都返回 true 时,结果返回为 true。在第一个 false 时,则停止执行计算。 +- `anyMatch(Predicate)`:如果流中的任意一个元素根据提供的 **Predicate** 返回 true 时,结果返回为 true。在第一个 false 是停止执行计算。 +- `noneMatch(Predicate)`:如果流的每个元素根据提供的 **Predicate** 都返回 false 时,结果返回为 true。在第一个 true 时停止执行计算。 -你已经在 `Prime.java` 中看到了 `noneMatch()` 的示例;` allMatch()` 和 `anyMatch()` 的用法基本上是等同的。让我们探究短路行为。为了创建消除冗余代码的 ` show()` 方法,我们必须首先发现如何统一地描述所有三个匹配器操作,然后将其转换为称作 **Matcher** 的接口: +我们已经在 `Prime.java` 中看到了 `noneMatch()` 的示例;`allMatch()` 和 `anyMatch()` 的用法基本上是等同的。下面我们来探究一下短路行为。为了消除冗余代码,我们创建了 `show()`。首先我们必须治到如何统一地描述这三个匹配器的操作,然后再将其转换为 **Matcher** 接口。代码示例: ```java // streams/Matching.java @@ -2001,15 +2001,17 @@ public class Matching { 1 2 3 4 5 6 7 8 9 true ``` -**BiPredicate** 是一个二元谓词,这意味着它只能接受两个参数并且只返回 true 或者 false。它的第一个参数是我们要测试的流,第二个参数是一个谓词 **Predicate**。因为 **Matcher** 适用于所有的 **Stream::*Match** 方法形式,所以我们可以传递每一个到 `show()` 中。`match.test()` 的调用会被转换成 **Stream::*Match** 函数的调用。 +**BiPredicate** 是一个二元断言,它只能接受两个参数且只返回 true 或者 false。它的第一个参数是我们要测试的流,第二个参数是一个断言 **Predicate**。**Matcher** 适用于所有的 **Stream::\*Match** 方法,所以我们可以传递每一个到 `show()` 中。`match.test()` 的调用会被转换成 **Stream::\*Match** 函数的调用。 -`show()` 获取两个参数,**Matcher** 匹配器和用于表示谓词测试 **n < val** 中最大值的 **val**。这个方法生成一个从 1 到 9 的整数流。`peek()` 是用于向我们展示测试在短路之前的情况。你可以在输出中发现每一次短路都会发生。 +`show()` 获取两个参数,**Matcher** 匹配器和用于表示断言测试 **n < val** 中最大值的 **val**。这个方法生成一个1-9之间的整数流。`peek()` 是用于向我们展示测试在短路之前的情况。从输出中可以看到每次都发生了短路。 -### 元素查找 +### 查找 -- `findFirst()`:返回一个含有第一个流元素的 **Optional**,如果流为空返回 **Optional.empty**。 +- `findFirst()`:返回第一个流元素的 **Optional**,如果流为空返回 **Optional.empty**。 - `findAny(`:返回含有任意流元素的 **Optional**,如果流为空返回 **Optional.empty**。 +代码示例: + ```java // streams/SelectElement.java import java.util.*; @@ -2038,7 +2040,7 @@ public class SelectElement { `findFirst()` 无论流是否为并行化的,总是会选择流中的第一个元素。对于非并行流,`findAny()`会选择流中的第一个元素(即使从定义上来看是选择任意元素)。在这个例子中,我们使用 `parallel()` 来并行流从而引入 `findAny()` 选择非第一个流元素的可能性。 -如果必须选择流中最后一个元素,那就使用 ` reduce()`: +如果必须选择流中最后一个元素,那就使用 `reduce()`。代码示例: ```java // streams/LastElement.java @@ -2076,7 +2078,7 @@ three - `max(Comparator)`:根据所传入的 **Comparator** 所决定的“最大”元素。 - `min(Comparator)`:根据所传入的 **Comparator** 所决定的“最小”元素。 -字符串类型有预先定义好的 **Comparator**,这简化了我们的示例: +**String** 类型有预设的 **Comparator** 实现。代码示例: ```java // streams/Informational.java @@ -2113,9 +2115,9 @@ you ### 数字流信息 - `average()` :求取流元素平均值。 -- `max()` 和 `min()`:因为这些操作在数字流上面,所以不需要 **Comparator**。 +- `max()` 和 `min()`:数值流操作无需 **Comparator**。 - `sum()`:对所有流元素进行求和。 -- `summaryStatistics()`:生成可能有用的数据。目前还不太清楚他们为什么觉得有必要这样做,因为你可以使用直接的方法产生所有的数据。 +- `summaryStatistics()`:生成可能有用的数据。目前并不太清楚这个方法存在的必要性,因为我们其实可以用更直接的方法获得需要的数据。 ```java // streams/NumericStreamInfo.java @@ -2142,11 +2144,11 @@ public class NumericStreamInfo { IntSummaryStatistics{count=100, sum=50794, min=8, average=507.940000, max=998} ``` -这些操作对于 **LongStream** 和 **DoubleStream** 也同样适用。 +上例操作对于 **LongStream** 和 **DoubleStream** 同样适用。 ## 本章小结 -流改变并极大地提升了 Java 编程的性质,并可能极大地阻止了 Java 编程人员向诸如 Scala 这种函数式语言的流动。在本书的剩余部分,我们将尽可能地使用流。 +流式操作改变并极大地提升了 Java 语言的可编程性,并可能极大地阻止了 Java 编程人员向诸如 Scala 这种函数式语言的流转。在本书的剩余部分,我们将尽可能地使用流。 diff --git a/docs/book/Appendix-Collection-Topics.md b/docs/book/Appendix-Collection-Topics.md index 22ce7a6..511de48 100644 --- a/docs/book/Appendix-Collection-Topics.md +++ b/docs/book/Appendix-Collection-Topics.md @@ -1613,7 +1613,7 @@ Brasilia | **Iterator\ iterator() Spliterator\ spliterator()** | 返回一个迭代器来遍历集合中的元素。 **Spliterators** 更复杂一些,它用在并发场景 | | **boolean remove(Object)** | 如果目标集合包含该参数,则在集合中删除该参数,如果成功删除则返回 **true** 。(“可选的”) | | **boolean removeAll(Collection\)** | 删除目标集合中,参数集合所包含的全部元素。如果有元素被成功删除则返回 **true** 。 (“可选的”) | -| **boolean removeIf(Predicate\)** | 删除此集合中,满足给定谓词(predicate)的所有元素 | +| **boolean removeIf(Predicate\)** | 删除此集合中,满足给定断言(predicate)的所有元素 | | **Stream\ stream() Stream\ parallelStream()** | 返回由该 **Collection** 中元素所组成的一个 **Stream** | | **int size()** | 返回集合中所包含元素的个数 | | **Object[] toArrat()** | 返回包含该集合所有元素的一个数组 |