实现
实现是用来存储对象集 的实际数据对象,它实现了在前面的章节中所描述的 核心 对象集 接口 。以下章节将描述三种实现:
通用实现
通用实现是公共类,它提供核心对象集接口的主要实现。
包装器实现
包装器实现与其它实现(通常为通用实现)一起提供附加功能。
便利实现
便利实现是小型实现,通常可通过静态方法(static factory methods)获得,它可方便、高效地为特殊 对象集 (象 singleton sets)替代通用实现。
另外,根据JDK的abstract implementations,你也可以建立自己的实现。这在一个单独的课程中有所描述,因为它属于高级课程。它不是特别难,但相对来讲,需要它的人很少。
通用实现
如下表格对通用实现做了小结。该表突出显示了它们的正常命名样式:名称均属于 形式, 这里的 是由类实现的核心对象集接口, 而 表示了在该实现底层的数据结构。
实现 | |||||
Hash Table | Resizable Array | Balanced Tree | Linked List | ||
接口 | Set | HashSet | TreeSet | ||
List | ArrayList | LinkedList | |||
Map | HashMap | TreeMap |
JDK 1.2 提供了每个接口的两种实现 (Collection是个例外,它没有直接的实现,但可当作其它 对象集 接口的最小公分母). 在每一个接口中,其中一种实现明显的是主实现: 要使用的那个,其它东西是一样的。主实现是 HashSet, ArrayList 和 HashMap. 注意SortedSet和SortedMap接口在上表中没有列出。它们各自都有一个实现,这些实现(TreeSet 和 TreeMap) 被列在 Set 和 Map 栏里。
这些实现不仅具有一致的名称,而且还有一致的行为。它们都实现所有的包含在它们的接口中的选项操作(optional operations),都允许 null 元素、键和值。每一种实现都是不同步的。它们都具有快速失效迭代功能, 这可以在迭代过程中检测非法并发更改,并快速彻底地失效,而不是冒在今后不可预知的时间里发生任意不可确定行为的风险。所有的实现都是可序列化的(Serializable), 并都支持公共 clone 方法。
新实现不同步这一事实代表与过去的一个中断:Vector 和 Hashtable 在 JDK 1.2 以前的版本中是同步的。之所以采用新的做法是因为 对象集 经常以一种同步在其中没有益处的方式所使用。这样的使用包括单线程使用、只读使用以及作为一个较大的数据对象(它有它自己的同步)的一部分而使用。通常,API设计的惯例是不让用户为不必要的性能花钱。再者,不必要的同步在某中情况下可能会导致死锁。
如果你需要一个同步的 对象集, synchronization wrappers(同步包装器)(将在 下一节介绍)允许任意 对象集 转换为同步对象集。因此,对新的 对象集 实现来说,当它对旧的 对象集 是强制性的时候,同步是可选的。 作为一个经验法则,你应该考虑的是接口,而不是实现。这就是为什么在这一节中没有编程实例的原因。对大部分情况来讲,实现的选择仅影响性能。首选的风格(就象在 接口课程 中所提到的)是在创建一个 对象集 时选择一个实现,并立即将一个新的 对象集 赋值给相应接口类型的一个变量(或将该 对象集 传递给参数为此接口类型的方法)。这样,程序将不会依赖于在一个给定的实现中任意增加的方法, 并给程序员和维护人员以迅速改变实现的自由(如果有关性能允许这样做的话)。
下面将简要讨论通用实现。实现的性能的描述使用词汇如 constant, log, linear, n log(n) 和 quadratic等。这些词汇指执行操作的关于时间复杂性(time complexity)的渐进上限(asymptotic upper bound)。 所有这些可够你消化的了,不过如果你不理解,也没太大关系。有兴趣的话可以阅读任意一本算法教科书,那上面有关于这类内容的解释。有一件事是应该牢记的,那就是:这种性能的度量有它的局限性。有时名义上较慢的实现对于你所实际使用大小的对象集来说可能要快。如果有些怀疑,估量一下该性能。