Skip to content

Commit 3098693

Browse files
committed
[ISSUE #40]校订流式编程 close #40
1 parent 2913b9b commit 3098693

File tree

3 files changed

+35
-33
lines changed

3 files changed

+35
-33
lines changed

docs/book/13-Functional-Programming.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -646,7 +646,7 @@ public class FunctionalAnnotation {
646646

647647
4. 如果返回值类型与参数类型一致,则是一个运算符:单个参数使用 `UnaryOperator`,两个参数使用 `BinaryOperator`
648648

649-
5. 如果接收两个参数且返回值为布尔值,则是一个谓词(Predicate)。
649+
5. 如果接收两个参数且返回值为布尔值,则是一个断言(Predicate)。
650650

651651
6. 如果接收的两个参数类型不同,则名称中有一个 `Bi`
652652

docs/book/14-Streams.md

Lines changed: 33 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1683,15 +1683,15 @@ Signal(dash)
16831683
## 终端操作
16841684

16851685

1686-
这些操作接收一个流并产生一个最终结果;它们不会向后端流提供任何东西。因此,终端操作总是你在管道中做的最后一件事情
1686+
以下操作将会获取流的最终结果。至此我们无法再继续往后传递流。可以说,终端操作总是我们在流管道中所做的最后一件事
16871687

16881688
<!-- Convert to an Array -->
16891689

1690-
### 转化为数组
1690+
### 数组
16911691
- `toArray()`:将流转换成适当类型的数组。
1692-
- `toArray(generator)`:在特殊情况下,生成器用于分配自定义的数组存储
1692+
- `toArray(generator)`:在特殊情况下,生成自定义类型的数组
16931693

1694-
这组方法在流操作产生的结果必须是数组形式时很有用。假如我们想在流里复用获取的随机数,可以将他们保存到数组中。代码示例:
1694+
当我们需要得到数组类型的数据以便于后续操作时,上面的方法就很有用。假设我们需要复用流产生的随机数时,就可以这么使用。代码示例:
16951695

16961696
```java
16971697
// streams/RandInts.java
@@ -1706,19 +1706,19 @@ public class RandInts {
17061706
}
17071707
```
17081708

1709-
上例将100个数值范围在 01000 之间的随机数流转换成为数组并将其存储在 `rints` 中,在每次调用 `rands()` 的时候可以重复获取相同的流
1709+
上例将100个数值范围在 01000 之间的随机数流转换成为数组并将其存储在 `rints` 中。这样一来,每次调用 `rands()` 的时候可以重复获取相同的整数流
17101710

17111711
<!-- Apply a Final Operation to Every Element -->
1712-
### 应用最终操作
1712+
### 循环
17131713

1714-
- `forEach(Consumer)`:你已经看到过很多次 `System.out::println` 作为 **Consumer** 函数。
1714+
- `forEach(Consumer)`常见如 `System.out::println` 作为 **Consumer** 函数。
17151715
- `forEachOrdered(Consumer)`: 保证 `forEach` 按照原始流顺序操作。
17161716

1717-
第一种形式:显式设计为任意顺序操作元素,仅在引入 `parallel()` 操作时才有意义。在 [并发编程](24-Concurrent-Programming.md) 章节之前我们不会深入研究这个问题。这里简单介绍下 `parallel()`:可实现多处理器并行操作。实现原理为将流分割为多个(通常数目为 CPU 核心数)并在不同处理器上分别执行操作。因为我们采用的是内部迭代,而不是外部迭代,所以这是可能实现的。
1717+
第一种形式:无序操作,仅在引入并行流时才有意义。在 [并发编程](24-Concurrent-Programming.md) 章节之前我们不会深入研究这个问题。这里简单介绍下 `parallel()`:可实现多处理器并行操作。实现原理为将流分割为多个(通常数目为 CPU 核心数)并在不同处理器上分别执行操作。因为我们采用的是内部迭代,而不是外部迭代,所以这是可能实现的。
17181718

17191719
`parallel()` 看似简单,实则棘手。更多内容将在稍后的 [并发编程](24-Concurrent-Programming.md) 章节中学习。
17201720

1721-
下例引入了 `parallel()` 来帮助理解 `forEachOrdered(Consumer)` 的作用和使用场景。代码示例:
1721+
下例引入 `parallel()` 来帮助理解 `forEachOrdered(Consumer)` 的作用和使用场景。代码示例:
17221722

17231723
```java
17241724
// streams/ForEach.java
@@ -1756,7 +1756,7 @@ public class ForEach {
17561756

17571757
<!-- Collecting -->
17581758

1759-
### 收集
1759+
### 集合
17601760

17611761
- `collect(Collector)`:使用 **Collector** 收集流元素到结果集合中。
17621762
- `collect(Supplier, BiConsumer, BiConsumer)`:同上,第一个参数 **Supplier** 创建了一个新结果集合,第二个参数 **BiConsumer** 将下一个元素包含到结果中,第三个参数 **BiConsumer** 用于将两个值组合起来。
@@ -1798,7 +1798,7 @@ stream, streams, throws, toCollection, trim, util,
17981798
void, words2]
17991799
```
18001800

1801-
**Files.**`lines()` 打开 **Path** 并将其转换成为行流。下一行代码将匹配一个或多个非单词字符(`\\w+`)行进行分割,然后使用 **Arrays.**`stream()` 将其转化成为流,并将结果扁平映射成为单词流。使用 `matches(\\d+)` 查找并移除全数字字符串(**注意**,`words2` 是通过的)。接下来我们使用 **String.**`trim()` 去除单词两边的空白,`filter()` 过滤所有长度小于3的单词,紧接着只获取100个单词,最后将其保存到 **TreeSet** 中。
1801+
**Files.**`lines()` 打开 **Path** 并将其转换成为行流。下一行代码将匹配一个或多个非单词字符(`\\w+`)行进行分割,然后使用 **Arrays.**`stream()` 将其转化成为流,并将结果展平映射成为单词流。使用 `matches(\\d+)` 查找并移除全数字字符串(**注意**,`words2` 是通过的)。接下来我们使用 **String.**`trim()` 去除单词两边的空白,`filter()` 过滤所有长度小于3的单词,紧接着只获取100个单词,最后将其保存到 **TreeSet** 中。
18021802

18031803
<!--
18041804
Files.lines() opens the Path and turns it into a Stream oflines. The next line splits those lines on boundaries of one or morenon-word characters (\\W+), which produces an array which is turnedinto a Stream with Arrays.stream(), and the result is flat-mapped back into a Stream of words. The matches(\\d+) finds
@@ -1858,7 +1858,7 @@ public class MapCollector {
18581858

18591859
在这里,我们只使用最简单形式的 `Collectors.toMap()`,这个方法值需要一个可以从流中获取键值对的函数。还有其他重载形式,其中一种形式是在遇到键值冲突时,需要一个函数来处理这种情况。
18601860

1861-
在大多数情况下,你可以在 `java.util.stream.Collectors`寻找到你想要的预先定义好的 **Collector**。在少数情况下当你找不到想要的时候,你可以使用第二种形式的 ` collect()`。 我基本上把它留作更高级的练习,但是这里有一个例子给出了基本想法
1861+
大多数情况下,`java.util.stream.Collectors` 中预设的 **Collector** 就能满足我们的要求。除此之外,你还可以使用第二种形式的 `collect()`。 我把它留作更高级的练习,下例给出基本用法
18621862

18631863
<!--
18641864
The capChars randomly-generated Iterator of capitalletters starts as a stream, then the iterator() method allows us touse it in the stream() method
@@ -1893,13 +1893,13 @@ cheese
18931893

18941894
<!-- Combining All Stream Elements -->
18951895

1896-
### 组合所有流元素
1896+
### 组合
18971897

18981898
- `reduce(BinaryOperator)`:使用 **BinaryOperator** 来组合所有流中的元素。因为流可能为空,其返回值为 **Optional**
18991899
- `reduce(identity, BinaryOperator)`:功能同上,但是使用 **identity** 作为其组合的初始值。因此如果流为空,**identity** 就是结果。
1900-
- `reduce(identity, BiFunction, BinaryOperator)`:这个形式更为复杂(所以我们不会介绍它),在这里被提到是因为它使用起来会更有效。通常,你可以显式地组合 `map()` 和 `reduce()` 来更简单的表达它。
1900+
- `reduce(identity, BiFunction, BinaryOperator)`:更复杂的使用形式(暂不介绍),这里把它包含在内,因为它可以提高效率。通常,我们可以显式地组合 `map()` 和 `reduce()` 来更简单的表达它。
19011901

1902-
如下是一个用于演示 `reduce()` 的示例
1902+
下面来看下 `reduce` 的代码示例
19031903

19041904
```java
19051905
// streams/Reduce.java
@@ -1946,20 +1946,20 @@ Frobnitz(7)
19461946
Frobnitz(29)
19471947
```
19481948

1949-
**Frobnitz** 包含了一个名为 `supply()` 的生成器;因为这个方法对于 `Supplier<Frobnitz>` 是签名兼容的,我们可以将其方法引用传递给 `Stream.generate()`(这种签名兼容性被称作结构一致性)。我们使用没有给“起始值”的 `reduce()`方法,这意味着它的返回值是 **Optional** 类型的。`Optional.ifPresent()` 只有在结果非空的时候才会调用 `Consumer<Frobnitz>` (`println` 方法可以被调用是因为 **Frobnitz** 可以通过 `toString()` 方法转换成 **String**)。
1949+
**Frobnitz** 包含了一个名为 `supply()` 的生成器;因为这个方法对于 `Supplier<Frobnitz>` 是签名兼容的,我们可以将其方法引用传递给 `Stream.generate()`(这种签名兼容性被称作结构一致性)。无“初始值”的 `reduce()`方法返回值是 **Optional** 类型。`Optional.ifPresent()` 只有在结果非空的时候才会调用 `Consumer<Frobnitz>` (`println` 方法可以被调用是因为 **Frobnitz** 可以通过 `toString()` 方法转换成 **String**)。
19501950

19511951
Lambda 表达式中的第一个参数 `fr0` 是上一次调用 `reduce()` 的结果。而第二个参数 `fr1` 是从流传递过来的值。
19521952

1953-
`reduce()` 中的 Lambda 表达式使用了三元表达式来获取结果,当其 size 小于 50 的时候获取 `fr0` 否则获取序列中的下一个值 `fr1`。因此你会取得第一个 size 小于 50 的 `Frobnitz`,只要找到了就这个结果就会紧紧地攥住它,即使有其他候选者出现。虽然这是一个非常奇怪的约束,但是它确实让你对 `reduce()` 有了更多的了解。
1953+
`reduce()` 中的 Lambda 表达式使用了三元表达式来获取结果,当其长度小于 50 的时候获取 `fr0` 否则获取序列中的下一个值 `fr1`。当取得第一个长度小于 50 的 `Frobnitz`,只要得到结果就会忽略其他。这是个非常奇怪的约束, 也确实让我们对 `reduce()` 有了更多的了解。
19541954

19551955
<!-- Matching -->
19561956
### 匹配
19571957

1958-
- `allMatch(Predicate)` :如果流的每个元素根据提供的 **Predicate** 都返回 true 时,结果返回为 true这个操作将会在第一个 false 之后短路;也就是不会在发生 false 之后继续执行计算
1959-
- `anyMatch(Predicate)`:如果流中的任意一个元素根据提供的 **Predicate** 返回 true 时,结果返回为 true这个操作将会在第一个 true 之后短路;也就是不会在发生 true 之后继续执行计算
1960-
- `noneMatch(Predicate)`:如果流的每个元素根据提供的 **Predicate** 都返回 false 时,结果返回为 true这个操作将会在第一个 true 之后短路;也就是不会在发生 true 之后继续执行计算
1958+
- `allMatch(Predicate)` :如果流的每个元素根据提供的 **Predicate** 都返回 true 时,结果返回为 true在第一个 false 时,则停止执行计算
1959+
- `anyMatch(Predicate)`:如果流中的任意一个元素根据提供的 **Predicate** 返回 true 时,结果返回为 true在第一个 false 是停止执行计算
1960+
- `noneMatch(Predicate)`:如果流的每个元素根据提供的 **Predicate** 都返回 false 时,结果返回为 true在第一个 true 时停止执行计算
19611961

1962-
你已经在 `Prime.java` 中看到了 `noneMatch()` 的示例;` allMatch()` 和 `anyMatch()` 的用法基本上是等同的。让我们探究短路行为。为了创建消除冗余代码的 ` show()` 方法,我们必须首先发现如何统一地描述所有三个匹配器操作,然后将其转换为称作 **Matcher** 的接口
1962+
我们已经在 `Prime.java` 中看到了 `noneMatch()` 的示例;`allMatch()` 和 `anyMatch()` 的用法基本上是等同的。下面我们来探究一下短路行为。为了消除冗余代码,我们创建了 `show()`。首先我们必须治到如何统一地描述这三个匹配器的操作,然后再将其转换为 **Matcher** 接口。代码示例
19631963

19641964
```java
19651965
// streams/Matching.java
@@ -2001,15 +2001,17 @@ public class Matching {
20012001
1 2 3 4 5 6 7 8 9 true
20022002
```
20032003

2004-
**BiPredicate** 是一个二元谓词,这意味着它只能接受两个参数并且只返回 true 或者 false。它的第一个参数是我们要测试的流,第二个参数是一个谓词 **Predicate**因为 **Matcher** 适用于所有的 **Stream::*Match** 方法形式,所以我们可以传递每一个到 `show()` 中。`match.test()` 的调用会被转换成 **Stream::*Match** 函数的调用。
2004+
**BiPredicate** 是一个二元断言,它只能接受两个参数且只返回 true 或者 false。它的第一个参数是我们要测试的流,第二个参数是一个断言 **Predicate****Matcher** 适用于所有的 **Stream::\*Match** 方法,所以我们可以传递每一个到 `show()` 中。`match.test()` 的调用会被转换成 **Stream::\*Match** 函数的调用。
20052005

2006-
`show()` 获取两个参数,**Matcher** 匹配器和用于表示谓词测试 **n < val** 中最大值的 **val**这个方法生成一个从 19 的整数流。`peek()` 是用于向我们展示测试在短路之前的情况。你可以在输出中发现每一次短路都会发生
2006+
`show()` 获取两个参数,**Matcher** 匹配器和用于表示断言测试 **n < val** 中最大值的 **val**这个方法生成一个1-9之间的整数流。`peek()` 是用于向我们展示测试在短路之前的情况。从输出中可以看到每次都发生了短路
20072007

2008-
### 元素查找
2008+
### 查找
20092009

2010-
- `findFirst()`:返回一个含有第一个流元素的 **Optional**,如果流为空返回 **Optional.empty**
2010+
- `findFirst()`:返回第一个流元素的 **Optional**,如果流为空返回 **Optional.empty**
20112011
- `findAny(`:返回含有任意流元素的 **Optional**,如果流为空返回 **Optional.empty**
20122012

2013+
代码示例:
2014+
20132015
```java
20142016
// streams/SelectElement.java
20152017
import java.util.*;
@@ -2038,7 +2040,7 @@ public class SelectElement {
20382040

20392041
`findFirst()` 无论流是否为并行化的,总是会选择流中的第一个元素。对于非并行流,`findAny()`会选择流中的第一个元素(即使从定义上来看是选择任意元素)。在这个例子中,我们使用 `parallel()` 来并行流从而引入 `findAny()` 选择非第一个流元素的可能性。
20402042

2041-
如果必须选择流中最后一个元素,那就使用 ` reduce()`:
2043+
如果必须选择流中最后一个元素,那就使用 `reduce()`。代码示例
20422044

20432045
```java
20442046
// streams/LastElement.java
@@ -2076,7 +2078,7 @@ three
20762078
- `max(Comparator)`:根据所传入的 **Comparator** 所决定的“最大”元素。
20772079
- `min(Comparator)`:根据所传入的 **Comparator** 所决定的“最小”元素。
20782080

2079-
字符串类型有预先定义好的 **Comparator**,这简化了我们的示例
2081+
**String** 类型有预设的 **Comparator** 实现。代码示例
20802082

20812083
```java
20822084
// streams/Informational.java
@@ -2113,9 +2115,9 @@ you
21132115
### 数字流信息
21142116

21152117
- `average()` :求取流元素平均值。
2116-
- `max()` 和 `min()`:因为这些操作在数字流上面,所以不需要 **Comparator**
2118+
- `max()` 和 `min()`:数值流操作无需 **Comparator**
21172119
- `sum()`:对所有流元素进行求和。
2118-
- `summaryStatistics()`:生成可能有用的数据。目前还不太清楚他们为什么觉得有必要这样做,因为你可以使用直接的方法产生所有的数据
2120+
- `summaryStatistics()`:生成可能有用的数据。目前并不太清楚这个方法存在的必要性,因为我们其实可以用更直接的方法获得需要的数据
21192121

21202122
```java
21212123
// streams/NumericStreamInfo.java
@@ -2142,11 +2144,11 @@ public class NumericStreamInfo {
21422144
IntSummaryStatistics{count=100, sum=50794, min=8, average=507.940000, max=998}
21432145
```
21442146

2145-
这些操作对于 **LongStream****DoubleStream** 也同样适用
2147+
上例操作对于 **LongStream****DoubleStream** 同样适用
21462148

21472149
## 本章小结
21482150

2149-
流改变并极大地提升了 Java 编程的性质,并可能极大地阻止了 Java 编程人员向诸如 Scala 这种函数式语言的流动。在本书的剩余部分,我们将尽可能地使用流。
2151+
流式操作改变并极大地提升了 Java 语言的可编程性,并可能极大地阻止了 Java 编程人员向诸如 Scala 这种函数式语言的流转。在本书的剩余部分,我们将尽可能地使用流。
21502152

21512153
<!-- 分页 -->
21522154

docs/book/Appendix-Collection-Topics.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1613,7 +1613,7 @@ Brasilia
16131613
| **Iterator\<T\> iterator() Spliterator\<T\> spliterator()** | 返回一个迭代器来遍历集合中的元素。 **Spliterators** 更复杂一些,它用在并发场景 |
16141614
| **boolean remove(Object)** | 如果目标集合包含该参数,则在集合中删除该参数,如果成功删除则返回 **true** 。(“可选的”) |
16151615
| **boolean removeAll(Collection\<?\>)** | 删除目标集合中,参数集合所包含的全部元素。如果有元素被成功删除则返回 **true** 。 (“可选的”) |
1616-
| **boolean removeIf(Predicate\<? super E\>)** | 删除此集合中,满足给定谓词(predicate)的所有元素 |
1616+
| **boolean removeIf(Predicate\<? super E\>)** | 删除此集合中,满足给定断言(predicate)的所有元素 |
16171617
| **Stream\<E\> stream() Stream\<E\> parallelStream()** | 返回由该 **Collection** 中元素所组成的一个 **Stream** |
16181618
| **int size()** | 返回集合中所包含元素的个数 |
16191619
| **Object[] toArrat()** | 返回包含该集合所有元素的一个数组 |

0 commit comments

Comments
 (0)