From 4c1cc4b77824edaad41701f7cc568bee784953e9 Mon Sep 17 00:00:00 2001 From: xiangflight Date: Thu, 18 Jul 2019 21:47:03 +0800 Subject: [PATCH] =?UTF-8?q?[revision=2012](=E6=88=AA=E8=87=B3=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E5=85=83=E7=B4=A0=E7=BB=84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/book/12-Collections.md | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/docs/book/12-Collections.md b/docs/book/12-Collections.md index 970f9fb..4bd425a 100644 --- a/docs/book/12-Collections.md +++ b/docs/book/12-Collections.md @@ -7,22 +7,22 @@ 通常,程序总是根据运行时才知道的某些条件去创建新的对象。在此之前,无法知道所需对象的数量甚至确切类型。为了解决这个普遍的编程问题,需要在任意时刻和任意位置创建任意数量的对象。因此,不能依靠创建命名的引用来持有每一个对象: ```java -MyType aReference +MyType aReference; ``` 因为从来不会知道实际需要多少个这样的引用。 大多数编程语言都提供了某种方法来解决这个基本问题。Java有多种方式保存对象(确切地说,是对象的引用)。例如前边曾经学习过的数组,它是编译器支持的类型。数组是保存一组对象的最有效的方式,如果想要保存一组基本类型数据,也推荐使用数组。但是数组具有固定的大小尺寸,而且在更一般的情况下,在写程序的时候并不知道将需要多少个对象,或者是否需要更复杂的方式来存储对象,因此数组尺寸固定这一限制就显得太过受限了。 -**java.util** 库提供了一套相当完整的*集合类*(collection classes)来解决这个问题,其中基本的类型有 **List** 、**Set** 、 **Queue** 和 **Map**。这些类型也被称作*容器类*(container classes),但我将使用Java类库使用的术语。集合提供了完善的方法来保存对象,可以使用这些工具来解决大量的问题。 +**java.util** 库提供了一套相当完整的*集合类*(collection classes)来解决这个问题,其中基本的类型有 **List** 、 **Set** 、 **Queue** 和 **Map**。这些类型也被称作*容器类*(container classes),但我将使用Java类库使用的术语。集合提供了完善的方法来保存对象,可以使用这些工具来解决大量的问题。 集合还有一些其它特性。例如, **Set** 对于每个值都只保存一个对象, **Map** 是一个关联数组,允许将某些对象与其他对象关联起来。Java集合类都可以自动地调整自己的大小。因此,与数组不同,在编程时,可以将任意数量的对象放置在集合中,而不用关心集合应该有多大。 -尽管在Java中没有直接的关键字支持,[^1]但集合类仍然是可以显著增强编程能力的基本工具。在本章中,将介绍Java集合类库的基本知识,并重点介绍一些典型用法。这里将专注于在日常编程中使用的集合。稍后,在[附录:集合主题]()中,还将学习到其余的那些集合和相关功能,以及如何使用它们的更多详细信息。 +尽管在 Java 中没有直接的关键字支持,[^1]但集合类仍然是可以显著增强编程能力的基本工具。在本章中,将介绍 Java 集合类库的基本知识,并重点介绍一些典型用法。这里将专注于在日常编程中使用的集合。稍后,在[附录:集合主题]()中,还将学习到其余的那些集合和相关功能,以及如何使用它们的更多详细信息。 ## 泛型和类型安全的集合 -使用Java SE5之前的集合的一个主要问题是编译器允许你向集合中插入不正确的类型。例如,考虑一个 **Apple** 对象的集合,这里使用最基本最可靠的 **ArrayList** 。现在,可以把 **ArrayList** 看作“可以自动扩充自身尺寸的数组”来看待。使用 **ArrayList** 相当简单:创建一个实例,用 **add()** 插入对象;然后用 **get()** 来访问这些对象,此时需要使用索引,就像数组那样,但是不需要方括号。[^2] **ArrayList** 还有一个 **size()** 方法,来说明集合中包含了多少个元素,所以不会不小心因数组越界而引发错误(通过抛出*运行时异常*,[异常]()章节介绍了异常)。 +使用 Java 5 之前的集合的一个主要问题是编译器允许你向集合中插入不正确的类型。例如,考虑一个 **Apple** 对象的集合,这里使用最基本最可靠的 **ArrayList** 。现在,可以把 **ArrayList** 看作“可以自动扩充自身尺寸的数组”来看待。使用 **ArrayList** 相当简单:创建一个实例,用 **add()** 插入对象;然后用 **get()** 来访问这些对象,此时需要使用索引,就像数组那样,但是不需要方括号。[^2] **ArrayList** 还有一个 **size()** 方法,来说明集合中包含了多少个元素,所以不会不小心因数组越界而引发错误(通过抛出*运行时异常*,[异常]()章节介绍了异常)。 在本例中, **Apple** 和 **Orange** 都被放到了集合中,然后将它们取出。正常情况下,Java编译器会给出警告,因为这个示例没有使用泛型。在这里,使用特定的注解来抑制警告信息。注解以“@”符号开头,可以带参数。这里的 **@SuppressWarning** 注解及其参数表示只抑制“unchecked”类型的警告([注解]()章节将介绍更多有关注解的信息): @@ -66,9 +66,9 @@ ndOrangesWithoutGenerics.java:23) **Apple** 和 **Orange** 是截然不同的,它们除了都是 **Object** 之外没有任何共同点(如果一个类没有显式地声明继承自哪个类,那么它就自动继承自 **Object**)。因为 **ArrayList** 保存的是 **Object** ,所以不仅可以通过 **ArrayList** 的 **add()** 方法将 **Apple** 对象放入这个集合,而且可以放入 **Orange** 对象,这无论在编译期还是运行时都不会有问题。当使用 **ArrayList** 的 **get()** 方法来取出你认为是 **Apple** 的对象时,得到的只是 **Object** 引用,必须将其转型为 **Apple**。然后需要将整个表达式用括号括起来,以便在调用 **Apple** 的 **id()** 方法之前,强制执行转型。否则,将会产生语法错误。 -在运行时,当尝试将 **Orange** 对象转为 **Apple**时,会出现输出中显示的错误。 +在运行时,当尝试将 **Orange** 对象转为 **Apple** 时,会出现输出中显示的错误。 -在[泛型]()章节中,你将了解到使用Java泛型来创建类可能很复杂。但是,使用预先定义的泛型类却相当简单。例如,要定义一个用于保存 **Apple** 对象的 **ArrayList** ,只需要使用 **ArrayList** 来代替 **ArrayList** 。尖括号括起来的是*类型参数*(可能会有多个),它指定了这个集合实例可以保存的类型。 +在[泛型]()章节中,你将了解到使用 Java 泛型来创建类可能很复杂。但是,使用预先定义的泛型类却相当简单。例如,要定义一个用于保存 **Apple** 对象的 **ArrayList** ,只需要使用 **ArrayList** 来代替 **ArrayList** 。尖括号括起来的是*类型参数*(可能会有多个),它指定了这个集合实例可以保存的类型。 通过使用泛型,就可以在编译期防止将错误类型的对象放置到集合中。[^3]下面还是这个示例,但是使用了泛型: ```java @@ -94,13 +94,13 @@ public class ApplesAndOrangesWithGenerics { */ ``` -在 **apples** 定义的右侧,可以看到 **new ArrayList<>()** 。这有时被称为“菱形语法”(diamond syntax)。在Java 7之前,必须要在两端都进行类型声明,如下所示: +在 **apples** 定义的右侧,可以看到 **new ArrayList<>()** 。这有时被称为“菱形语法”(diamond syntax)。在 Java 7 之前,必须要在两端都进行类型声明,如下所示: ```java ArrayList apples = new ArrayList(); ``` -随着类型变得越来越复杂,这种重复产生的代码非常混乱且难以阅读。程序员发现所有类型信息都可以从左侧获得,因此,编译器没有理由强迫右侧在重复这些。Java语言团队采纳了这种*类型推断*(type inference)的请求,即使只是这么小的一点。 +随着类型变得越来越复杂,这种重复产生的代码非常混乱且难以阅读。程序员发现所有类型信息都可以从左侧获得,因此,编译器没有理由强迫右侧再重复这些。Java语言团队采纳了这种*类型推断*(type inference)的请求,即使只是这么小的一点。 有了 **ArrayList** 声明中的类型指定,编译器会阻止将 **Orange** 放入 **apples** ,因此,这会成为一个编译期错误而不是运行时错误。 @@ -142,9 +142,9 @@ Braeburn@4e25154f ## 基本概念 -Java集合类库采用“保持对象”(holding objects)的思想,并将其分为两个不同的概念,表示为类库的基本接口: +Java集合类库采用“持有对象”(holding objects)的思想,并将其分为两个不同的概念,表示为类库的基本接口: -1. **集合(Collection)** :一个独立元素的序列,这些元素都服从一条或多条规则。 **List** 必须以插入的方式保存元素, **Set** 不能包含重复元素, **Queue** 按照*排队规则*来确定对象产生的顺序(通常与它们被插入的顺序相同)。 +1. **集合(Collection)** :一个独立元素的序列,这些元素都服从一条或多条规则。**List** 必须以插入的顺序保存元素, **Set** 不能包含重复元素, **Queue** 按照*排队规则*来确定对象产生的顺序(通常与它们被插入的顺序相同)。 2. **映射(Map)** : 一组成对的“键值对”对象,允许使用键来查找值。 **ArrayList** 使用数字来查找对象,因此在某种意义上讲,它是将数字和对象关联在一起。 **map**允许我们使用一个对象来查找另一个对象,它也被称作*关联数组*(associative array),因为它将对象和其它对象关联在一起;或者称作*字典*(dictionary),因为可以使用一个键对象来查找值对象,就像在字典中使用单词查找定义一样。 **Map**s是强大的编程工具。 尽管并非总是可行,但在理想情况下,你编写的大部分代码都在与这些接口打交道,并且唯一需要指定所使用的精确类型的地方就是在创建的时候。因此,可以像下面这样创建一个 **List** : @@ -189,9 +189,12 @@ public class SimpleCollection { 可以使用 *for-in* 语法来遍历所有的 **Collection** ,就像这里所展示的那样。在本章的后续部分,还将学习到一个更灵活的概念,*迭代器*。 + ## 添加元素组 -在**java.util**包中的 **Arrays** 和 **Collections** 类中都有很多实用的方法,可以在一个 **Collection** 中添加一组元素。 **Arrays.asList()** 方法接受一个数组或是逗号分隔的元素列表(使用可变参数),并将其转换为 **List** 对象。 **Collections.addAll()** 方法接受一个 **Collection** 对象,以及一个数组或是一个逗号分隔的列表,将其中元素添加到 **Collection** 中。下边的示例展示了这两个方法,以及更通用的 **addAll()** 方法,所有 **Collection** 类型都包含该方法: +在 **java.util** 包中的 **Arrays** 和 **Collections** 类中都有很多实用的方法,可以在一个 **Collection** 中添加一组元素。 + +**Arrays.asList()** 方法接受一个数组或是逗号分隔的元素列表(使用可变参数),并将其转换为 **List** 对象。 **Collections.addAll()** 方法接受一个 **Collection** 对象,以及一个数组或是一个逗号分隔的列表,将其中元素添加到 **Collection** 中。下边的示例展示了这两个方法,以及更通用的 **addAll()** 方法,所有 **Collection** 类型都包含该方法: ```java // collections/AddingGroups.java @@ -716,7 +719,7 @@ pets.removeLast(): Hamster ``` **Pets.list()** 的结果被传递给 **LinkedList** 的构造器,以便使用它来填充 **LinkedList** 。如果查看 **Queue** 接口就会发现,它在 **LinkedList** 的基础上添加了 **element()** , **offer()** , **peek()** , **poll()** 和 **remove()** 方法,以使其可以成为一个 **Queue** 的实现。 **Queue** 的完整示例将在本章稍后给出。 - + ## 堆栈Stack