Update 47. 优先使用Collection而不是Stream来作为方法的返回类型.md

This commit is contained in:
Joe 2020-04-04 21:40:17 +08:00 committed by GitHub
parent 329dbd040c
commit a538261504
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -2,9 +2,9 @@
  许多方法返回元素序列sequence。在 Java 8 之前,通常方法的返回类型是 `Collection``Set` 和 `List` 这些接口;还包括 `Iterable` 和数组类型。通常很容易决定返回哪一种类型。规范norm是返回 `Collection` 接口。如果该方法仅用于启用 for-each 循环,或者返回的序列不能实现某些 `Collection` 方法 (通常是 `contains(Object)`),则使用迭代(`Iterable`)接口。如果返回的元素是基本类型或有严格的性能要求,则使用数组。在 Java 8 中将流Stream添加到平台中这使得为序列返回方法选择适当的返回类型的任务变得非常复杂。
  你可能听说过,流现在是返回元素序列的明显的选择,但是正如第 45 条所讨论的,流不会使迭代过时:编写好的代码需要明智地将流和迭代结合起来。如果一个 API 只返回一个流,并且一些用户想用 for-each 循环遍历返回的序列,那么这些用户肯定会感到不安。这尤其令人沮丧,因为 `Stream` 接口只有一个抽象方法的返回值只 `Iterable``Stream` 的方法规范与 `Iterable` 兼容。阻止程序员使用 for-each 循环在流上迭代的唯一原因是 `Stream` 无法继承 `Iterable`
  你可能听说过,流现在是返回元素序列的明显的选择,但是正如第 45 条所讨论的,流不会使迭代过时:编写好的代码需要明智地将流和迭代结合起来。如果一个 API 只返回一个流,并且一些用户想用 for-each 循环遍历返回的序列,那么这些用户肯定会感到不安。这尤其令人沮丧,因为 `Stream` 接口有一个和 `Iterable` 接口中一样的抽象方法,并且 `Stream` 的方法规范与 `Iterable` 中的一致。阻止程序员使用 for-each 循环在流上迭代的唯一原因是 `Stream` 无法继承 `Iterable`
  遗憾的是,这个问题没有好的解决方法。 乍一看,似乎可以将方法引用传递给 `Stream` 的 iterator 方法。 结果代码可能有点嘈杂和不透明,但并非不合理:
  遗憾的是,这个问题没有好的解决方法。 乍一看,似乎可以将方法引用传递给 `Stream` 的 iterator 方法。 结果代码可能有些乱,但并非不合理:
```java
// Won't compile, due to limitations on Java's type inference
@ -20,14 +20,14 @@ Test.java:6: error: method reference not expected here
for (ProcessHandle ph : ProcessHandle.allProcesses()::iterator) {
```
  为了使代码编译,必须将方法引用强制转换为适当参数化`Iterable` 类型:
  为了使代码编译,必须将方法引用强制转换为对应参数`Iterable` 类型:
```java
// Hideous workaround to iterate over a stream
for (ProcessHandle ph : (Iterable<ProcessHandle>)ProcessHandle.allProcesses()::iterator)
```
  此代码有效,但在实践中使用它太嘈杂和不透明。 更好的解决方法是使用适配器方法。 JDK 没有提供这样的方法,但是使用上面的代码片段中使用的相同技术,很容易编写一个方法。 请注意,在适配器方法中不需要强制转换,因为 Java 的类型推断在此上下文中能够正常工作:
  此代码可以工作,但在实践中使用它太乱。 更好的解决方法是使用适配器方法。 JDK 没有提供这样的方法,但是使用上面的代码片段中的相同技术,很容易编写一个方法。 请注意,在适配器方法中不需要强制转换,因为 Java 的类型推断在此上下文中能够正常工作:
```java
// Adapter from Stream<E> to Iterable<E>
@ -36,7 +36,7 @@ public static <E> Iterable<E> iterableOf(Stream<E> stream) {
}
```
  使用此适配器,可以使用 for-each 语句迭代任何流:
  通过这个适配器,你可以使用 for-each 语句迭代任何流:
```java
for (ProcessHandle p : iterableOf(ProcessHandle.allProcesses())) {
@ -44,7 +44,7 @@ for (ProcessHandle p : iterableOf(ProcessHandle.allProcesses())) {
}
```
  注意,条目 34 中的 `Anagrams` 程序的流版本使用 `Files.lines` 方法读取字典,而迭代版本使用了 scanner。`Files.lines` 方法优于 scannerscanner 在读取文件时无声地吞噬所有异常。理想情况下,我们也会在迭代版本中使用 `Files.lines`。如果 API 只提供对序列的流访问,而程序员希望使用 for-each 语句遍历序列,那么他们就要做出这种妥协。
  注意,第 34 条中的 `Anagrams` 程序的流版本使用 `Files.lines` 方法读取字典,而迭代版本使用了 scanner。`Files.lines` 方法优于 scannerscanner 在读取文件时无声地吞噬所有异常。理想情况下,我们也会在迭代版本中使用 `Files.lines`。如果 API 只提供对序列的流访问,而程序员希望使用 for-each 语句遍历序列,那么他们就要做出这种妥协。
  相反,如果一个程序员想要使用流管道来处理一个序列,那么一个只提供 `Iterable` 的 API 会让他感到不安。JDK 同样没有提供适配器,但是编写这个适配器非常简单:
@ -57,9 +57,9 @@ public static <E> Stream<E> streamOf(Iterable<E> iterable) {
  如果你正在编写一个返回对象序列的方法,并且它只会在流管道中使用,那么当然可以自由地返回流。类似地,返回仅用于迭代的序列的方法应该返回一个 `Iterable`。但是如果你写一个公共 API它返回一个序列你应该为用户提供哪些想写流管道哪些想写 for-each 语句,除非你有充分的理由相信大多数用户想要使用相同的机制。
  `Collection` 接口是 `Iterable` 的子类型,并且具有 `stream` 方法,因此它提供迭代和流访问。 因此,**`Collection` 或适当的子类型通常是公共序列返回方法的最佳返回类型。** 数组使用 `Arrays.asList``Stream.of` 方法提供简单的迭代和流访问。 如果返回的序列小到足以容易地放入内存中,那么最好返回一个标准集合实现,例如 `ArrayList``HashSet`。 **但是不要在内存中存储大的序列,只是为了将它作为集合返回。**
  `Collection` 接口是 `Iterable` 的子类型,并且具有 `stream` 方法,因此它可以同时提供迭代和流访问的能力。 因此,**`Collection` 或适当的子类型通常是公共序列返回方法的最佳返回类型。** 数组使用 `Arrays.asList``Stream.of` 方法提供简单的迭代和流访问能力。 如果返回的序列小到足以容易地放入内存中,那么最好返回一个标准集合实现,例如 `ArrayList``HashSet`。 **但是不要在只是为了将它作为集合返回,而在内存中存储很大的序列。**
  如果返回的序列很大但可以简洁地表示,请考虑实现一个专用集合。 例如假设返回给定集合的幂集power set就是原集合中所有的子集包括全集和空集构成的集族该集包含其所有子集。 {abc} 的幂集为 {{}{a}{b}{c}{ab}{ac}{bc}{ab, c}}。 如果一个集合具有 n 个元素,则幂集具有 2n 个。 因此,你甚至不应考虑将幂集存储在标准集合实现中。 但是,在 `AbstractList` 的帮助下,很容易为此实现自定义集合。
  如果你需要返回一很大但可以简洁地表示的序列,请考虑实现一个专用集合。 例如假设返回给定集合的幂集power set就是原集合中所有的子集包括全集和空集构成的集族该集包含其所有子集。 {abc} 的幂集为 {{}{a}{b}{c}{ab}{ac}{bc}{ab, c}}。 如果一个集合具有 n 个元素,则幂集具有 2n 个。 因此,你甚至不应考虑将幂集存储在标准集合实现中。 但是,在 `AbstractList` 的帮助下,很容易为此实现自定义集合。
  诀窍是使用幂集中每个元素的索引作为位向量bit vector其中索引中的第 n 位指示源集合中是否存在第 n 个元素。 本质上,从 0 到 2n-1 的二进制数和 n 个元素集和的幂集之间存在自然映射。 这是代码: