JDK 5.0 中最吸引人的地方在于集合框架的一些最突出的特性上,例如:支持泛型的语言级别上的新变化,以及可以在 java.util.concurrent
包中找到的并发集合工具包。实际上,以前在 developerWorks 上的“驯服 Tiger: 并发集合”和“介绍 JDK 5.0 中的泛型”这两篇教程中介绍了上述特性。但是其他增强还没有得到足够的重视。在本文中,我将研究其他三个变化:更新过的 Arrays
和 Collections
类、新的 Queue
接口以及它的 PriorityQueue
实现。
数组(Array)
Arrays
类提供了一系列处理数组的静态工具方法,这些索引的数据结构的大小是固定的。在 5.0 版本之前,Arrays
类拥有针对原始数据库类型和通用 Object
类型的每种不同数组类型的 binarySearch()
、equals()
、fill()
和 sort()
方法。用于将 Object
数组转换成 List
的附加 asList()
方法仍然有用。Tiger 为所有数组添加了 hashCode()
和 toString()
方法,还添加了特定于 Object
数组的 deepEquals()
、deepHashCode()
和 deepToString()
方法。总计有 21 个新方法可用:
public static boolean deepEquals(Object[] a1, Object[] a2)
public static int deepHashCode(Object[] a)
public static String deepToString(Object[] a)
public static int hashCode(boolean[] a)
public static int hashCode(byte[] a)
public static int hashCode(char[] a)
public static int hashCode(double[] a)
public static int hashCode(float[] a)
public static int hashCode(int[] a)
public static int hashCode(long[] a)
public static int hashCode(Object[] a)
public static int hashCode(short[] a)
public static String toString(boolean[] a)
public static String toString(byte[] a)
public static String toString(char[] a)
public static String toString(double[] a)
public static String toString(float[] a)
public static String toString(int[] a)
public static String toString(long[] a)
public static String toString(Object[] a)
public static String toString(short[] a)
自从集合框架初次出现在 J2SE 1.2 中以来,人们第一次对实用工具类进行了更改。我无法确定为什么 Sun 要等这么久才进行更改,但是对于可用的帮助器方法系列来说,这些更改是受欢迎的添加。
新添加的第一个方法是 hashCode()
。对于任意数组类型,都可以调用 Arrays.hashCode(arrayVar)
方法来获得格式良好的哈希码。这个哈希码可以用作 HashMap
或者其他相关目的的键。如果您不知道如何生成良好的哈希码,那么最好使用 Arrays
类,它能产生更少冲突。Arrays 类生成等价于拥有相同元素的 List
的代码。
在创建自己的类时,既需要提供 equals()
方法,又需要提供 hashCode()
方法。在 Arrays
的新方法 hashCode()
的帮助下,可以为任何本地数组类型生成哈希码,而不用在每次需要它的时候折腾您自己。
所有数组类型都可用的另一个方法是 toString()
。对于任何数组类型,都可以调用 Arrays.toString(arrayVar)
获得逗号分隔的元素列表,列表用方括号包围,如清单 1 的程序所示:
|
清单 2 显示了结果:
|
新的 deepEquals()
、deepHashCode()
和 deepToString()
方法的工作方式类似于它们那些非深度(non-deep)的同类,但它们不仅会停下手来处理顶级数组的每个元素,还会更深入地研究生成结果的多维数组。
虽然不是一个新方法,但 asList()
方法在 5.0 的工作方式有所不同。以前,这个方法接受 Object[]
数组作为它的参数。现在,因为 Tiger 的可变参数列表特性,任何用逗号分隔的列表都可以接受,如清单 3 所示:
|
如果传递给命令行的元素不同,清单 3 中的两个示例没必要产生同样的结果 ,但是它确实展示了 Tiger 在语言级别上的变化如何扩展了 Arrays
原有的 asList()
方法。
|
集合
Arrays
用于处理不同集合的辅助类是 Collections
类。同样,这个类也不是一个新类,但是该类的特性已经针对 5.0 作了扩展。现在有 13 个新方法:
checkedCollection()
checkedSet()
checkedSortedSet()
checkedList()
checkedMap()
checkedSortedMap()
emptySet()
emptyList()
emptyMap()
reverseOrder()
frequency()
disjoint()
addAll()
其中 6 个 checked*()
方法工作起来与 6 个 synchronized*()
和 unmodifiable*()
方法类似。使用 synchronized*()
方法时,要向该方法提供一个集合,然后该方法将返回同一个集合的同步的、线程安全的版本。使用 unmodifiable*()
方法时,得到的是指定集合的只读视图。除了集合参数之外,checked*()
操作可能还要求第二个或者第三个参数(如清单 4 所示),并返回该集合的动态的类型安全视图:
|
使用 Java 5.0 平台,您可能以为:由于将集合声明为通用集合 (Collection<String> c = new HashSet<String>();
),所以不需要进行运行时检测了。但是如果向工具方法传递 String
版本的 HashSet
,而工具方法只能处理非通用的 Set
,那么该方法可能就会错误地向集合添加一个非 String
元素。通过临时修改程序,用 Collection<String> c = Collections.checkedCollection(new HashSet<String>(), String.class);
添加运行时检查,您可以迅速发现问题的根源。
三个 empty*()
方法 —— emptySet()
、emptyList()
和 emptyMap()
—— 生成空的不可改变的集合。虽然也可以用 new ArraySet()
这样的方法创建空集合,但是还要通过某个 unmodifiable*()
方法才能确保新集合是不可改变的。empty 方法用更理想的方式提供了空的只读集合。
|
队列(Queue)接口
5.0 集合框架较大的一个改变就是添加了新的基接口 Queue
。虽然这个接口是在“并发集合”技巧 (请参阅 参考资料)中描述的,但它的应用并不限于并发。在计算机科学中,队列数据结构是基本的先进先出(FIFO) 结构。项目添加到尾部,并且要从顶部删除。不仅能添加和删除元素,还能查看队列中有哪些元素。清单 5 显示了 Queue
接口的 5 个方法:
|
请记住,Queue
是从 Collection
接口扩展的,所以实现 Queue
接口也就实现了 Collection
。在使用 Queue
的实现时,应当将自己限制在接口的方法上。例如,向 Queue
添加元素可以用 Collection
的 add()
方法来实现,它在失败时会抛出未检测异常。相反,如果大小有限的队列满了,那么 offer()
方法会返回 false
,而不需要处理队列满的异常。
java.util.concurrent
包中具有 Queue
接口的多个实现,但并不包含所有实现。LinkedList
类针对 JDK 5.0 的 Queue
接口作了修正,而 PriorityQueue
是随 JDK 5.0 添加进来的。余下的实现 —— ArrayBlockingQueue
、ConcurrentLinkedQueue
、DelayQueue
、LinkedBlockingQueue
、PriorityBlockingQueue
和 SynchronousQueue
—— 都是 java.util.concurrent
包的组成部分。
因为 LinkedList
不是新事物,所以我们来看一下新的 PriorityQueue
类。如清单 6 所示,可以用 6 种方法创建它。在不能使用 Comparator
时,可以使用元素的自然顺序来确定优先级。如果元素没有实现 Comparable
接口,那么就会产生运行时错误:
|
为了演示 PriorityQueue
的用法,清单 7 中的程序添加了所有命令行元素,并按字母顺序处理它们。由于队列结构是 LinkedList
,所以顺序应当是典型的 FIFO 顺序,但是 PriorityQueue
将根据优先级对元素进行排序:
|
清单 8 显示了用命令行 one two three four
运行程序之后的输出 :
|
关于新的 Queue
接口,有件事需要提一下,这件事与 Collections
类有关:方法 checkedQueue()
、emptyQueue()
、synchronizedQueue()
和 unmodifiableQueue()
全都是 Collections
类中所缺少的。根据 bug 报告,除了 checkedQueue()
之外,所有类都是故意缺失的。对于 synchronizedQueue()
,并发集合是比纯粹的包装器更好的选择。其他方法则被认为不是必需的。也许 Tiger/6.0 版本中会添加 checkedQueue()
(和 checkedBlockingQueue()
) 。