# 第二章 抽象数据类型

> 译者：[Allenyep](https://github.com/Allenyep)

本书中包含大多数课程中的经典数据结构，如一些具有代表性的数据集合框架。这意味着其中包括了一些可能使这些值变得有序值的集合或多元集合[1]。其中一些数据集合是关联索引的；它们是搜索结构，拥有类似于将某些索引值（键）映射到其他数据（例如将名称映射到街道地址）的函数。

我们可以通过介绍不同数据结构的操作集来描述抽象的情况-即通过描述可用的抽象数据类型。从程序的角度来看，它需要某种数据集合用于操作，而这一系列操作方法必须是大家都应该知道的。

对于每种不同的抽象数据类型，通常有几种可能的实现方式。选择哪个数据结构取决于你的程序需要处理多少数据，处理数据的速度有多快，以及对存储空间的限制。对于很多程序来说，这有点像一个肮脏的秘密交易，你选择实现的方式完全不重要。然而，知识丰富的程序员应该熟悉这些可用的工具。

我希望你们中的许多人会发现这一章令人沮丧，因为它将谈论数据类型的*接口*，而不是谈论它们背后的实现方法。习惯它。毕竟，在这套标准库背后有广泛使用的编程语言将会以一组接口的方式呈现给你。包括：函数中参数传递的方向以及一些注释,通常是使用英语来描述接口的作用。作为一名开发者，您将花费大量时间来生成向您的客户提供相同功能的模块。

## 2.1 迭代器

如果我们要开发一些数据集合的概念，我们必须回答这样一个问题:我们如何从这样的数据集合中获取对应项？你可能已经熟悉了一种集合结构——数组。获取其中元素十分简单。例如，要打印数组的内容，您可以编写

``` java
for (int i = 0; i < A.length; i += 1)
    System.out.print (A[i] + ", ");
```
数组有n个元素,所以这样的循环很容易。但是其他集合呢？例如罐子里"第一个硬币"是哪一个呢？即使我们任意选择给一个集合中的每个元素一个编号,我们将会发现"选取第n个元素"代价将会十分昂贵（想象一下Scheme当中的lists）。

尝试对每个集合强加编号以提取其中元素的问题在于它迫使集合的实现者提供比我们的问题可能需要的更加具体的工具。这是一个经典的工程权衡问题：满足一个约束(如获取第n个元素)可能会有其他代价(逐个迭代的代价十分高)。

所以问题是在不依赖索引或者可能完全没有依赖顺序的情况下提供集合中的元素。Java提供了两种方法作为接口。接口**java.util.Iterator**提供了一种以某种顺序访问集合中所有项目的方法。**java.util.ListIterator**提供以某种特定顺序访问集合中的元素的方法,但没有为每个元素分配索引\[2\]。

\[2\]:该库还定义了接口java.util.Enumeration，它本质上是同一想法的旧版本。我们不会在这里谈论这个界面，因为最重要的位置是新的程序首选的是迭代器

### 2.1.1 迭代器接口

Java库定义了一个接口:**java.util.Iterator**,如代码2.1所示，表示为“在集合中所有元素排序的东西”的概念，但不保证顺序。这只是一个Java接口,它不具备实现类。在Java库中，代表数据项集合以提供对这些元素进行排序的方法的类的标准方法是定义诸如

``` java
Iterator<SomeType> iterator () { ... }
```
它分配并返回一个迭代器(如代码3.3）。通常这个迭代器的实际类型将被隐藏(甚至为private);所有使用这个类的用户都需要知道的是**iterator**返回的对象提供了两个方法**hasNext**和**next**(有时候是**remove**)。例如，打印字符串集合的所有元素的一般方法(类似于以前的阵列打印机)是：

``` java
for (Iterator<String> i = C.iterator ();i.hasNext (); )
    System.out.print (i.next () + " ");
```

``` java
package java.util;
/** An object that delivers each item in some collection of items* each of which is a T. */
public interface Iterator <T> {
    /** True iff there are more items to deliver. */
    boolean hasNext ();
    /** Advance THIS to the next item and return it. */
    T next ();
    /** Remove the last item delivered by next() from the collection* being iterated over. Optional operation: may throw* UnsupportedOperationException if removal is not possible. */
    void remove ();
    }
```
代码2.1 **java.util.Iterator**接口

编写这个循环的程序员不需要知道对象i必须经历什么样的迭代以产生所请求的元素。即使代表其集合的C发生重大变化也不需要对循环进行修改。

这种特殊类型的for循环在Java2中是如此常见和有用，版本1.5中它有自己的“语法糖”，称为增强for循环。你可以编写如下代码

``` java
for (String i : C)
    System.out.print (i + " ");
```
获得与前一个for循环相同的效果。Java将插入缺失的部分，将其转换为

``` java
for (Iterator<String> ρ = C.iterator (); ρ.hasNext(); ) {
    String i = ρ.next ();
    System.out.println (i + " ");
}
```

其中ρ是编译器引入的一些新变量，在程序的其他地方未使用，其类型取自**C.iterator()**。这个增强的for循环适用于任何取自对象接口**java.lang.Iterable**的对象C。简单地定义

``` java
public interface Iterable<T> {
    Iterator<T> iterator ();
}
```
感谢增强for循环，只需在类型定义上定义迭代器方法，就提供了一种非常方便的方法来对序列中包含的该类型的任何子对象进行排序。
不用说，为迭代器(Iterators)引入了这种方便的简写，Java的设计者突然有这样一个观点：迭代遍历数组的元素比迭代遍历库类的更加笨拙。因此，他们将增强的for语句扩展为包含数组。例如，这两种方法是等价的：

``` java
/** The sum of the* elements of A */
int sum (int[] A) {
    int S;S = 0;
    for (int x : A) 
        S += x;
}

/** The sum of the elements* of A */
int sum (int[] A) {
    int S;
    S = 0;
    for (int κ = 0; κ < A.length; κ++){
        int x = A[κ];
        S += x;
    }
}
```

### 2.1.2 ListIterator接口
有些数据集合的确有排序的自然概念，但从按索引的集合中提取任意项的代价可能仍然很昂贵。例如，您可能看到了Scheme语言中的链表：给定列表中的一个元素，它需要n个操作来确定第n个后续元素（与Java数组相反，Java数组只需要一个步骤或一些特定操就可以检索任何项。）。标准Java库包含接口**java.util.ListIterator**，它通过有序序列进行排序的，而不通过索引来获取每个序列。如代码2.2所示。除了迭代器的“查询”方法和**remove**方法之外，**Listaterator**类提供了在集合中插入新项或替换项的操作。

## 2.2 Java抽象集合框架

Java库（从JDK 1.2开始）提供代表各种集合的接口层次结构，加上抽象类的层次结构，以帮助程序员提供这些接口的实现，以及一些实际（“具体”）的实现。这些类都在包**java.util**中找到。图2.4说明了专门用于集合的类和接口的层次结构。

### 2.2.1 集合框架接口

Java库接口**java.util.Collection**其方法总结在图2.5和2.6中，描述了包含值集合的数据结构，其中每个值都是对某个Object（或null）的引用。与“集合（Collection）”相对的术语“集（set）”也出现在这里，因为Collection可以像数学上定义的集合那样描述多集合（bags）。

``` java
package java.util;
/** Abstraction of a position in an ordered collection. At any* given time, THIS represents a position (called its cursor)* that is just after some number of items of type T (0 or more) of* a particular collection, called the underlying collection. */
public interface ListIterator<T> extends Iterator<T> {
    /* Exceptions: Methods that return items from the collection throw* NoSuchElementException if there is no appropriate item. Optional* methods throw UnsupportedOperationException if the method is not* supported. */
    /* Required metho ds: */
    /** True unless THIS is past the last item of the collection */
    boolean hasNext ();
    /** True unless THIS is before the first item of the collection */
    boolean hasPrevious ();
    /** Returns the item immediately after the cursor, and* moves the current position to just after that item.* Throws NoSuchElementException if there is no such item. */
    T next ();
    /** Returns the item immediately before the cursor, and* moves the current position to just before that item.* Throws NoSuchElementException if there is no such item. */
    T previous ();
    /** The number of items before the cursor */
    int nextIndex ();
    /* nextIndex () - 1 */
    int previousIndex ();
    /* Optional methods: *//** Insert item X into the underlying collection immediately before* the cursor (X will be returned by previous()). */
    void add (T x);
    /** Remove the item returned by the most recent call to .next ()* or .previous (). There must not have been a more recent* call to .add(). */
    void remove ();
    /** Replace the item returned by the most recent call to .next ()* or .previous () with X in the underlying collection.* There must not have been a more recent call to .add() or .remove. */
    void set (T x);
}
```
代码2.2 java.util.ListIterator接口

【图2.3】
图2.3 Java库的Map相关类型（来自java.util）。椭圆表示接口;虚线框是抽象类，实心框是具体（非抽象）类。实线箭头表示继承(extends)关系，虚线箭头表示实现(implements)关系。抽象类供希望添加新集合类的实现用。它们提供了某些方法的默认实现。程序员将new应用于具体类以获取实例，并且（理想情况下）使用接口作为形式参数类型，以便尽可能广泛地使用方法。

【图2.4】
图2.4 Java库的Collection类型（来自java.util）.有关符号，请参见图2.3

由于这是一个接口，描述操作的文档注释可能不准确。一个无能或恶作剧的程序员可以编写一个实现Collection的类，将add方法改成remove方法。尽管如此，正经的程序员都会根据注释接受Collection的方法编写程序。C作为一个正常的集合，在执行C.add(x)之后，x将被放进C中。

并非每种集合都需要实现每种方法。具体而言，图2.6中不是可选方法，但可能会抛出选择提升标准异常UnsupportedOperationException。有关此特定设计选择的进一步讨论参见2.5节。仅实现所需方法的类本质上是只读集合;它们一旦被创造就无法修改。

关于图2.5中的构造函数的注释仅仅是一个注释。Java接口没有构造函数，因为它们不代表具体对象的特定类型。然而你首先需要一些构造函数来创建一个Collection，而注释的目的是告诉你一些有用的统一标准。

在这一点上，你可能想知道Collection类可能是什么样的，因为不可能直接创建一个它的实例（它是一个接口），并且缺少有关其成员的功能的详细信息（例如，给定的Collection可以有两个相同的元素吗？）。重点是使用Collection接口中提供的信息编写的任何函数都适用于Collection的所有实现类。

例如，这里有一个简单的方法来确定一个Collection的元素是否是另一个的子集：

``` java
/** True iff C0 is a subset of C1, ignoring repetitions. */
public static boolean subsetOf (Collection<?> C0, Collection<?> C1) {
    for (Object i : C0)
        if (! C1.contains (i))
            return false;
    // Note: equivalent to
    // for (Iterator<?> iter = C0.iterator(); iter.hasNext (); ) {
        // Object i = iter.next ();
        // ...
        return true;
    }
```

我们不知道C0和C1是什么类型的对象(它们可能是完全不同的Collection实现类),也不清楚他们的迭代器以什么顺序提供元素，是否允许重复。这些方法仅依赖于接口及其注释中描述的属性，并且总是能达到想要的效果（假如这些程序员是通过实现Collection去完成他们的工作）。

``` java
package java.util;
/** A collection of values, each an Object reference. */
public interface Collection<T> extends Iterable<T> {
    /* Constructors. Classes that implement Collection should* have at least two constructors:* CLASS (): Constructs an empty CLASS* CLASS (C): Where C is any Collection, constructs a CLASS that* contains the same elements as C. *//* Required met hods: *//** The number of values in THIS. */
    int size ();
    /** True iff size () == 0. */
    boolean isEmpty ();
    /** True iff THIS contains X: that is, if for some z in* THIS, either z and X are null, or z.equals (X). */
    boolean contains (Object x);
    /** True iff contains(x) for all elements x in C. */
    boolean containsAll (Collection<?> c);
    /** An iterator that yields all the elements of THIS, in some* order. */
    Iterator<T> iterator ();
    /** A new array containing all elements of THIS. */
    Object[] toArray ();
    /** Assuming ANARRAY has dynamic type T[] (where T is some* reference type), the result is an array of type T[] containing* all elements of THIS. The result is ANARRAY itself, if all of* these elements fit (leftover elements of ANARRAY are set to null).* Otherwise, the result is a new array. It is an error if not* all items in THIS are assignable to T. */
    <T> T[] toArray (T[] anArray);

    // Interface java.util.Collection, continued.
    /* Optional methods. Any of these may do nothing except to* throw UnsupportedOperationException. */
    /** Cause X to be contained in THIS. Returns true if the Collection *
    /* changes as a result. */
    boolean add (T x);
    /** Cause all members of C to be contained in THIS. Returns true* if the object THIS changes as a result. */
    boolean addAll (Collection<? extends T> c);
    /** Remove all members of THIS. */
    void clear ();
    /** Remove a Object .equal to X from THIS, if one exists,* returning true iff the object THIS changes as a result. */
    boolean remove (Object X);
    /** Remove all elements, x, such that C.contains(x) (if any* are present), returning true iff there were any* objects removed. */
    boolean removeAll (Collection<?> c);
    /** Intersection: Remove all elements, x, such that C.contains(x)* is false, returning true iff any items were removed. */
    boolean retainAll (Collection<?> c);}
```
代码2.5 接口java.util.Collection。必须方法和可选方法。

### 2.2.2 Set接口

在数学中，集合是没有重复的值的集合。这也是接口java.util.Set的定义。然而这个规定并不是以Java接口的形式直接表达的。实际上就Java编译器而言，以下是一个非常好的定义：

``` java
package java.util;
public interface Set<T> extends Collection<T> { }
```

这些方法都是一样的。它们的不同之处都在注释中。各元素的单拷贝规则在几个方法中更具体的注释中反映出来。结果如代码2.7所示。在这个定义中，我们还包括equals和hashCode方法。这些方法自动成为任何接口的一部分，因为它们是在Java类java.lang.Object中定义的，但我把它们包含在这里是因为它们的语义规范（注释）比一般对象更严格。当然，equals是来表示集合相等。我们将在第7章详解hashCode。

### 2.2.3 List接口

由于该术语是在Java库中使用的，list是有序元素集合，可能具有重复元素。也就是说，它是一种特殊的Collection，其中有一个元素的序列，第一个元素，最后一个元素，第n个元素，并且其中元素可能出现重复(和Set不一样)。因此，包含对有序序列的其他方法的扩展接口（相对于Collection）是有意义的。代码2.8展示了该接口。

大量方法包含在listIterator类及其返回的对象中。从接口描述中可以看到，可以使用列表(List)接口本身的方法插入、添加、删除或排序列表中的项。或者你也可以通过使用listIterator创建一个列表迭代器。使用listIterator来处理整个列表（或其中的某些部分）通常比使用List和其他List的方法更快，它使用数字索引来表示需要的元素。

**视图**

subList方法十分特别。诸如L.subList（i，j）的调用通过返回值以产生另一个List(通常与L不同)，它由L的（j-1）个项组成。此外，它是通过提供L的这一部分的视图来实现的，即访问相同数据容器的另一种方式。这个设计思路是修改子列表（使用添加，删除和设置等方法）也应该修改L的相应部分。例如，要删除列表L中除第一个k项以外的所有项，您可以编写

``` java
L.subList (k, L.size ()).clear ();
```

``` java
package java.util;
/** A Collection that contains at most one null item and in which no* two distinct non-null items are .equal. The effects of modifying* an item contained in a Set so as to change the value of .equal* on it are undefined. */
public interface Set<T> extends Collection<T> {
    /* Constructors. Classes that implement Set should* have at least two constructors:* CLASS (): Constructs an empty CLASS* CLASS (C): Where C is any Collection, constructs a CLASS that* contains the same elements as C, with duplicates removed. */
    /** Cause X to be contained in THIS. Returns true iff X was *
    /* not previously a member. */
    boolean add (T x);
    /** True iff S is a Set (instanceof Set) and is equal to THIS as a* set (size()==S.size() each of item in S is contained in THIS). */
    boolean equals (Object S);
    /** The sum of the values of x.hashCode () for all x in THIS, with* the hashCode of null taken to be 0. */
    int hashCode ();
    /* Other methods inherited from Collection:* size, isEmpty, contains, containsAll, iterator, toArray,* addAll, clear, remove, removeAll, retainAll */
}
```
代码2.7 接口java.util.Set.只展示比Collection更具体的注释的方法。

``` java
package java.util;
/** An ordered sequence of items, indexed by numbers 0 .. N-1,* where N is the size() of the List. */
public interface List<T> extends Collection<T> {
    /* Required metho ds: *//** The Kth element of THIS, where 0 <= K < size(). Throws* IndexOutOfBoundsException if K is out of range. */
    T get (int k);
    /** The first value k such that get(k) is null if X==null,* X.equals (get(k)), otherwise, or -1 if there is no such k. */
    int indexOf (Object x);
    /** The largest value k such that get(k) is null if X==null,* X.equals (get(k)), otherwise, or -1 if there is no such k. */
    int lastIndexOf (Object x);
    /* NOTE: The methods iterator, listIterator, and subList produce* views that become invalid if THIS is structurally modified by* any other means (see text). */
    /** An iterator that yields all the elements of THIS, in proper* index order. (NOTE: it is always valid for iterator() to* return the same value as would listIterator, below.) */
    Iterator<T> iterator ();
    /** A ListIterator that yields the elements K, K+1, ..., size()-1* of THIS, in that order, where 0 <= K <= size(). Throws* IndexOutOfBoundsException if K is out of range. */
    ListIterator<T> listIterator (int k);
    /** Same as listIterator (0) */
    ListIterator<T> listIterator ();
    /** A view of THIS consisting of the elements L, L+1, ..., U-1,* in that order. Throws IndexOutOfBoundsException unless* 0 <= L <= U <= size(). */
    List<T> subList (int L, int U);
    /* Other methods inherited from Collection:* add, addAll, size, isEmpty, contains, containsAll, remove, toArray */
```
代码2.8 接口java.util.List的方法，以及从Collection继承的方法

因此，List上有很多的操作，不需要定义就能使用，因为它是对子列表操作的结果。例如不需要手写从i到j删除的remove方法，或者通过索引搜索下标k的元素的indexOf方法。

迭代器（包括ListIterators）提供了另一个视图集合的示例。同样可以通过其方法提供的迭代器访问或修改Collection的当前内容。就此而言，任何Collect都可以是一个视图 - “身份视图”。

每当同一实体有两个可能的视图时，使用其中一个来修改实体会有可能干扰另一个视图。在一个视图中的变化不仅仅是原视图中，应该在其他视图中看到（如上面清除子列表的示例），但是当被查看的实体被其他方式改变时，一些视图的快速直接实现可能会出错。在迭代器上调用remove方法时应该发生什么，根据Iterator的规范，应该删除的元素是否已经被直接删除（通过在完整的Collection上调用remove方法）。假设有一个包含某些完整List元素2到4的子列表。如果清除完整List，然后添加3个元素，子列表视图中的内容是什么？

由于存在这些问题，通过除该视图之外的某些方式，其生成方法的完整规范（在List接口中是iterator，listIterator和subList）都规定，如果基础List在结构上进行了修改（如果添加或删除了元素），则视图将变为无效。因此，如果执行L.add（...），则L.iterator（）的结果将变为无效，或者如果你执行从L生成的其他迭代器或子列表中的remove方法也是如此。然而，我们还会遇到一些视图，例如由Map中的值方法（参见图2.12）生成的视图，这些视图应该保持有效，即使在对象中进行结构修改时也是如此;对于Map的实现来说，设计者认为这是有必要的。

### 2.2.4 有序Set

List接口描述了描述序列的数据类型，在这些序列中，程序员通过将序列中的元素添加到序列中的顺序或位置明确地表明序列中元素的顺序。相比之下，SortedSet接口旨在描述数据根据某些选定关系确定排序的序列。当然，这里出现了一个问题：在Java中，我们如何表示这个“选定关系”，以便我们可以定义它？我们如何将排序关系作为参数？

**排序：Comparable和Comparator接口**

函数有多种方法来定义某些set对象的排序。一种方法是用具有明显的含义来定义布尔运算如equals, less, greater等。C族语言（包括Java）中的库倾向于将所有这些组合成一个单独的函数，该函数用整数返回值表示关系。例如，在String类型上，x.compareTo("cat")返回一个零，负或正整数，具体取决于x是否等于“cat”，是否以词典顺序出现在它之前，或者在它之后出现。因此，Strings上的排序x≤y对应于条件x.compareTo（y）<= 0。

``` java
package java.lang;
/** Describes types that have a natural ordering. */public interface Comparable<T> {
    /** Returns* 
    * a negative value iff THIS < Y under the natural ordering* 
    * a positive value iff THIS > Y;* 
    * 0 iff X and Y are "equivalent".* 
    Throws ClassCastException if X and Y are incomparable. */
    int compareTo (T y);
}
```
代码2.9 接口java.lang.Comparable，它标记了自然排序的类。

出于SortedSet接口的目的，由compareTo方法（或者是compare）表示的此≤（或≥）排序旨在是总排序。也就是说，它是有传递性的（x≤y且y≤z意味着x≤z），自反性的(x ≤ x)，反对称的（x≤y且y≤x表示x等于y）。此外，对于函数域中的所有x和y，x≤y或者y≤x。

一些类（如字符串String）定义了自己的标准比较操作。这样做的标准方法是实现Comparable接口，如代码2.9所示。然而，并非所有的类都有这样的排序，自然排序也不一定是在任何给定情况下想要的。例如，可以按字典顺序、颠倒字典顺序或不区分大小写的顺序对字符串排序。

在Scheme语言中，没有这样一个特别的问题：排序关系只是一个函数，在Schame中函数是非常好的值。在一定程度上，在C和Fortran等语言中也是如此，在这些语言中，函数可以用作子程序的参数，但Schame不同，它只能访问全局变量（Java中称为静态作用域或类变量）。Java不直接支持函数作为值，但事实证明，这不是一个限制。Java标准库将Comparator接口（图2.10）定义为可用作排序关系的表示。

这两个接口提供的方法应该是适当的总排序。但是，Java语言实际上不能强制执行任何条件。它们只是注释当中强加的惯例。程序员违反这些设定的可能会导致各种意外。同样，没有什么可以阻止你定义与.equals函数不一致的比较操作。我们说compare（或compareTo）与equals是一致的，如果x.equals（y）和 C.compare（x，y）== 0。在没有充分理由的情况下保持这种一致性通常是好的做法。

``` java
package java.util;
/** An ordering relation on certain pairs of objects. If */
public interface Comparator<T> {
    /** Returns* * a negative value iff X < Y according to THIS ordering;* * a positive value iff X > Y;* * 0 iff X and Y are "equivalent" under the order;* Throws ClassCastException if X and Y are incomparable.*/
    int compare (T x, T y);
    /** True if ORD is "same" ordering as THIS. It is legal to return* false (conservatively) even if ORD does define the same ordering,* but should return true only if ORD.compare (X, Y) and* THIS.compare(X, Y) always have the same value. */
    boolean equals (Object ord);
}
```
代码2.10 接口java.util.Comparator，表示对象之间的排序关系

**SortedSet接口**

代码2.11中的SortedSet接口扩展了Set接口以便于它的迭代器方法提供了一个Iterator，对其内容排序。它还提供了额外的方法，只有在特别的顺序时才能使用。有两种方法来定义这种排序：通过程序员提供一个比较器构建一个排序集以确定顺序，或者集合的内容必须是可比较的，并且能使用它们的确定顺序。

## 2.3 Java Map Abstractions

术语map或mapping在计算机科学和其他学科上用作数学意义的函数的同义词，一些集合Set（域）中的项目与另一集合（隐藏域）之间的对应关系，域中的每个项目对应于（映射到）单个隐藏域元素中。（域的任何数量的成员（包括零）都可以对应于隐藏域的给定成员。由域的某个成员映射到的隐藏域的子集称为映射范围或映射下的域图。）

在程序开发中，从操作角度看典型的情况是，通过类似于Map的数据结构“查找”给定的键（域值），以确定相关的值（隐藏域值）。然而，从数学的角度来看，完美的解释是mapping是一组set集，(d, c)，其中d为键和c为值。

``` java
package java.util;
public interface SortedSet<T> extends Set<T> {
    /* Constructors. Classes that implement SortedSet should define* at least the constructors* CLASS (): An empty set ordered by natural order (compareTo).* CLASS (CMP): An empty set ordered by the Comparator CMP.* CLASS (C): A set containing the items in Collection C, in* natural order.* CLASS (S): A set containing a copy of SortedSet S, with the* same order.*/
    /** The comparator used by THIS, or null if natural ordering used. */
    Comparator<? super T> comparator ();
    /** The first (smallest) item in THIS according to its ordering */
    T first ();
    /** The last (largest) item in THIS according to its ordering */
    T last ();
    /* NOTE: The methods headSet, tailSet, and subSet produce* views that become invalid if THIS is structurally modified by* any other means. *//** A view of all items in THIS that are strictly less than X. */
    SortedSet<T> headSet (T x);
    /** A view of all items in THIS that are strictly >= X. */
    SortedSet<T> tailSet (T x);
    /** A view of all items, y, in THIS such that X0 <= y < X1. */
    SortedSet<T> subSet (T X0, T X1);
}
```
代码2.11 java.util.SortedSet接口

### 2.3.1 Map接口

标准Java库使用代码2.12和2.13中显示的java.util.Map接口来展示这些“mapping”概念。此接口提供Map的视图作为查找操作（使用get方法），而且Map的视图是一组有序对（使用方法entrySet）。同时又要求“有序对”反向表示，由嵌套接口Map.Entry可以显示。因此，希望引入新类型映射的程序员不仅可以实现Map接口的具体类，而且可以实现Map.Entry。

### 2.3.2 SortedMap接口

实现java.util.SortedMap的对象应该是一个按键排序的Map，正如图2.15所示，操作类似于接口Sorted Set的操作。

## 2.4 一个例子

考虑一系列名称(ni, mi)读取的问题。我们希望创建所有第一个成员的列表，ni，按字母顺序排列，并且对于他们每个值，都是与他们配对的所有值mi，每个m出现一次，并按照第一次出现的顺序列出。因此，输入：

John Mary， George Jeff， Tom Bert， George Paul， John Peter，Tom Jim， George Paul， Ann Cyril， John Mary， George Eric

输出：

Ann: Cyril<br>
George: Jeff Paul Eric<br>
John: Mary Peter<br>
Tom: Bert Jim

我们可以使用某种SortedMap来处理ni和用List每个处理mi。 一种可能的方法（将Reader作为输入源，将PrintWriter作为输出）如代码2.16所示。

``` java
package java.util;
public interface Map<Key, Val> {
    /* Constructors: Classes that implement Map should* have at least two constructors:* CLASS (): Constructs an empty CLASS* CLASS (M): Where M is any Map, constructs a CLASS that* denotes the same abstract mapping as C. */
    /* Required metho ds: */
    /** The number of keys in the domain of THIS map. */
    int size ();
    /** True iff size () == 0 */
    boolean isEmpty ();
    /* NOTE: The methods keySet, values, and entrySet produce views* that remain valid even if THIS is structurally modified. */
    /** The domain of THIS. */
    Set<Key> keySet ();
    /** The range of THIS. */
    Collection<Val> values ();
    /** A view of THIS as the set of all its (key,value) pairs. */
    Set<Map.Entry<Key, Val>> entrySet ();
    /** The value mapped to by KEY, or null if KEY is not* in the domain of THIS. */
    /** True iff keySet().contains (KEY) */
    boolean containsKey (Object key);
    /** True iff values().contains (VAL). */
    boolean containsValue (Object val);Object get (Object key);
    /** True iff M is a Map and THIS and M represent the same mapping. */
    boolean equals (Object M);
    /** The sum of the hashCode values of all members of entrySet(). */
    int hashCode ();
    static interface Entry { ... 
    // See Figure 2.14 


    // Interface java.util.Map, continued
    /* Optional methods: */
    /** Set the domain of THIS to the empty set. */
    void clear();
    /** Cause get(KEY) to yield VAL, without disturbing other values. */
    Object put(Key key, Val val);
    /** Add all members of M.entrySet() to the entrySet() of THIS. */
    void putAll(Map<? extends Key, ? extends Val> M);
    /** Remove KEY from the domain of THIS. */
    Object remove(Object key);}
}
``` 
代码2.12 接口java.util.Map的必需方法和可选方法

``` java
/** Represents a (key,value) pair from some Map. In general, an Entry* is associated with a particular underlying Map value. Operations that* change the Entry (specifically setValue) are reflected in that* Map. Once an entry has been removed from a Map as a result of* remove or clear, further operations on it may fail. */
static interface Entry<Key,Val> {
    /** The key part of THIS. */
    Key getKey();
    /** The value part of THIS. */
    Val getValue();
    /** Cause getValue() to become VAL, returning the previous value. */
    Val setValue(Val val);
    /** True iff E is a Map.Entry and both represent the same (key,value)* pair (i.e., keys are both null, or are .equal, and likewise for* values).*/
    boolean equals(Object e);
    /** An integer hash value that depends only on the hashCode values* of getKey() and getValue() according to the formula:* (getKey() == null ? 0 : getKey().hashCode ())* ^ (getValue() == null ? 0 : getValue.hashCode ()) */
    int hashCode();}
```
代码2.14 嵌套接口java.util.Map.Entry，它与java.util.Map接口嵌套

``` java
package java.util;
public interface SortedMap<Key,Val> extends Map<Key,Val> {
    /* Constructors: Classes that implement SortedMap should
    * have at least four constructors:
    * CLASS (): An empty map whose keys are ordered by natural order.
    * CLASS (CMP): An empty map whose keys are ordered by the Comparator CMP.
    * CLASS (M): A map that is a copy of Map M, with keys ordered
    * in natural order.
    * CLASS (S): A map containing a copy of SortedMap S, with
    * keys obeying the same ordering.*/
    /** The comparator used by THIS, or null if natural ordering used. */
    Comparator<? super Key> comparator ();
    /** The first (smallest) key in the domain of THIS according to* its ordering */
    Key firstKey ();
    /** The last (largest) item in the domain of THIS according to* its ordering */
    Key lastKey ();
    /* NOTE: The methods headMap, tailMap, and subMap produce views* that remain valid even if THIS is structurally modified. *//** A view of THIS consisting of the restriction to all keys in the* domain that are strictly less than KEY. */
    SortedMap<Key,Val> headMap (Key key);
    /** A view of THIS consisting of the restriction to all keys in the* domain that are greater than or equal to KEY. */
    SortedMap<Key,Val> tailMap (Key key);
    /** A view of THIS restricted to the domain of all keys, y,* such that KEY0 <= y < KEY1. */
    SortedMap<Key,Val> subMap (Key key0, Key key1);
}
```
代码2.15 接口java.util.SortedMap，显示Map中未包含的方法

``` java
import java.util.*;
import java.io.*;
class Example {
    /** Read (ni, mi) pairs from INP, and summarize all* pairings for each $n_i$ in order on OUT. */
    static void correlate (Reader inp, PrintWriter out)throws IOException{
    Scanner scn = new Scanner (inp);
    SortedMap<String, List<String>> associatesMap= new TreeMap<String,List<String>> ();
    while (scn.hasNext ()) {
        String n = scn.next ();
        String m = scn.next ();
        if (m == null || n == null)
            throw new IOException ("bad input format");
        List<String> associates = associatesMap.get (n);
        if (associates == null) {
            associates = new ArrayList<String> ();
            associatesMap.put (n, associates);
            }
        if (! associates.contains (m))
            associates.add (m);
    }
    for (Map.Entry<String, List<String>> e : associatesMap.entrySet ()) {
        System.out.format ("%s:", e.getKey ());
        for (String s : e.getValue ())
            System.out.format (" %s", s);System.out.println ();
        }
    }
}
```
代码2.16 SortedMaps和Lists的例子

## 2.5 管理部分实现：设计选项

在整个Collection接口中，可以看到（注释中）某些操作是“可选的”。具体的限制让实现方法需要使用

``` java
throw new UnsupportedOperationException ();
```

作为方法的一部分。这是一种不优雅的实现方式，但它提出了一个重要的设计问题。抛出异常就是动态行为。通常，编译器不会警告程序必然会抛出这样的异常；只有在测试了程序之后，才会发现某些数据结构选择的实现无效。

另一种设计会将接口分成更小的部分，如下所示：

``` java 
public interface ConstantIterator<T> {
Req uired methods of Iterator
}
public interface Iterator<T> extends ConstantIterator<T> {
    void remove ();
    }
public interface ConstantCollection<T> {
Req uired methods of Collection
}
public interface Collection<T> extends ConstantCollection<T> {
Optional methods of Collection
}
public interface ConstantSet<T> extends ConstantCollection<T> {
}
public interface Set<T> extends ConstantSet<T>, Collection<T> {
}
public interface ConstantList<T> extends ConstantCollection<T> {
    Req uired methods of List
}
public interface List<T> extends Collection<T>, ConstantList<T> {
    Optional methods of List
}

etc.. . 
```

使用这样的设计，编译器可以捕获尝试调用不支持的方法，因此您不需要测试来发现实现中的差距。

但是，这种重新设计会有成本。它不像上面列出的那样简单。比如说，ConstantList中的subList方法。这将清晰地返回一个ConstantList，因为如果不允许更改列表，则不允许您更改其中的视图。然而这意味着类型List需要两个subList方法（带有不同名称），一个继承自ConstantList的方法，另一个返回允许修改产生的List。类似的思路适用于迭代器方法的结果，有两个返回方法，一个返回ConstantIterator类型，另一个返回Iterator类型。此外，这个方案的实现并没有解决List的实现问题，即允许添加元素或者清除所有元素，但是不能删除单个元素。为此仍然需要UnsupportedOperationException或更复杂的异常处理类嵌套。

显然，Java设计者决定保留通过测试发现的一些问题，以简化其库的设计。相比之下，C++中相应标准库的设计者选择区分适用于任何集合的操作和仅适用于“可变”集合的操作。然而他们没有用接口设计他们的库;在C++库中引入新类型的集合（collection）或映射（map）显得十分尴尬。
