From a538261504259e5f6c31292e147d8cd8b8ea94f2 Mon Sep 17 00:00:00 2001 From: Joe <736777445@qq.com> Date: Sat, 4 Apr 2020 21:40:17 +0800 Subject: [PATCH] =?UTF-8?q?Update=2047.=20=E4=BC=98=E5=85=88=E4=BD=BF?= =?UTF-8?q?=E7=94=A8Collection=E8=80=8C=E4=B8=8D=E6=98=AFStream=E6=9D=A5?= =?UTF-8?q?=E4=BD=9C=E4=B8=BA=E6=96=B9=E6=B3=95=E7=9A=84=E8=BF=94=E5=9B=9E?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...用Collection而不是Stream来作为方法的返回类型.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/notes/47. 优先使用Collection而不是Stream来作为方法的返回类型.md b/docs/notes/47. 优先使用Collection而不是Stream来作为方法的返回类型.md index cd6f9fa..217f903 100644 --- a/docs/notes/47. 优先使用Collection而不是Stream来作为方法的返回类型.md +++ b/docs/notes/47. 优先使用Collection而不是Stream来作为方法的返回类型.md @@ -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.allProcesses()::iterator) ``` -  此代码有效,但在实践中使用它太嘈杂和不透明。 更好的解决方法是使用适配器方法。 JDK 没有提供这样的方法,但是使用上面的代码片段中使用的相同技术,很容易编写一个方法。 请注意,在适配器方法中不需要强制转换,因为 Java 的类型推断在此上下文中能够正常工作: +  此代码可以工作,但在实践中使用它太乱。 更好的解决方法是使用适配器方法。 JDK 没有提供这样的方法,但是使用上面的代码片段中的相同技术,很容易编写一个方法。 请注意,在适配器方法中不需要强制转换,因为 Java 的类型推断在此上下文中能够正常工作: ```java // Adapter from Stream to Iterable @@ -36,7 +36,7 @@ public static Iterable iterableOf(Stream 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` 方法优于 scanner,scanner 在读取文件时无声地吞噬所有异常。理想情况下,我们也会在迭代版本中使用 `Files.lines`。如果 API 只提供对序列的流访问,而程序员希望使用 for-each 语句遍历序列,那么他们就要做出这种妥协。 +  注意,第 34 条中的 `Anagrams` 程序的流版本使用 `Files.lines` 方法读取字典,而迭代版本使用了 scanner。`Files.lines` 方法优于 scanner,scanner 在读取文件时无声地吞噬所有异常。理想情况下,我们也会在迭代版本中使用 `Files.lines`。如果 API 只提供对序列的流访问,而程序员希望使用 for-each 语句遍历序列,那么他们就要做出这种妥协。   相反,如果一个程序员想要使用流管道来处理一个序列,那么一个只提供 `Iterable` 的 API 会让他感到不安。JDK 同样没有提供适配器,但是编写这个适配器非常简单: @@ -57,9 +57,9 @@ public static Stream streamOf(Iterable 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:就是原集合中所有的子集(包括全集和空集)构成的集族),该集包含其所有子集。 {a,b,c} 的幂集为 {{},{a},{b},{c},{a,b},{a,c},{b,c},{a,b, c}}。 如果一个集合具有 n 个元素,则幂集具有 2n 个。 因此,你甚至不应考虑将幂集存储在标准集合实现中。 但是,在 `AbstractList` 的帮助下,很容易为此实现自定义集合。 +  如果你需要返回一很大但可以简洁地表示的序列,请考虑实现一个专用集合。 例如,假设返回给定集合的幂集(power set:就是原集合中所有的子集(包括全集和空集)构成的集族),该集包含其所有子集。 {a,b,c} 的幂集为 {{},{a},{b},{c},{a,b},{a,c},{b,c},{a,b, c}}。 如果一个集合具有 n 个元素,则幂集具有 2n 个。 因此,你甚至不应考虑将幂集存储在标准集合实现中。 但是,在 `AbstractList` 的帮助下,很容易为此实现自定义集合。   诀窍是使用幂集中每个元素的索引作为位向量(bit vector),其中索引中的第 n 位指示源集合中是否存在第 n 个元素。 本质上,从 0 到 2n-1 的二进制数和 n 个元素集和的幂集之间存在自然映射。 这是代码: