The lion's share of interest in JDK 5.0 has been on the more prominent features of the Collections Framework, like the new language-level changes to support generics and the concurrent collections utility libraries found in the java.util.concurrent package. In fact, prior developerWorks content covered just that in "Concurrent collections" and the "Introduction to generic types in JDK 5.0" tutorial. But other enhancements haven't been given nearly enough attention. In this article, I'll look at three other changes: the updated Arrays and Collections classes and the new Queue interface, with its PriorityQueue implementation.
The Arrays class provides a series of static utility methods for working with arrays, those indexed data structures that are of a fixed size. Prior to 5.0, the class had binarySearch(), equals(), fill(), and sort() methods for each of the different array types you could have for primitive data types and a generic Object type. An additional asList() method for converting an Object array to a List is still available. Tiger adds to the set with hashCode() and toString() for all and deepEquals(), deepHashCode(), and deepToString() methods specific to Object arrays. In total, 21 new methods are available:
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)
This change to the utility class is the first since the Collections Framework debuted in J2SE 1.2. I'm not sure why Sun waited so long to introduce the changes, but they are welcome additions to the series of available helper methods.
The first of the new methods added is hashCode(). For any array type, you can call Arrays.hashCode(arrayVar) and get a well formed hash code. This hash code could be used as a key into a HashMap or for any other related purpose. Unless you know how to generate a good hash code, the one generated from the Arrays class is bound to be better, resulting in fewer collisions. It happens to generate a code that is equivalent to having a List of the same elements.
When creating your own classes, you are supposed to provide both equals() and hashCode() methods together. With the help of the new hashCode() method of Arrays, you can use it to generate a hash code for any local array types, instead of rolling your own each time you need it.
The other method that is now available for all array types is toString(). For any array type, you can call Arrays.toString(arrayVar) to get a comma-separated list of elements, surrounded by square brackets, as shown by the program in Listing 1:
Listing 1. Stringifying with Arrays.toString
import java.util.Arrays;
public class ArgsToString {
public static void main(String args[]) {
System.out.println(Arrays.toString(args));
}
}
|
Listing 2 shows the results:
Listing 2. Results from Listing 1
>java ArgsToString One Two Three [One, Two, Three] |
The new deepEquals(), deepHashCode(), and deepToString() methods work like their non-deep counterparts, but instead of stopping and working with each element of the top-level array, they continue to dig deeper into the multi-dimensional array to generate results.
While not a new method, the asList() method does actually behave differently with 5.0. Previously, the method accepted an Object[] array as its argument. Now, because of the variable-argument list features of Tiger, any comma-separated list will work, as shown in Listing 3:
Listing 3. Arrays.asList differences
import java.util.Arrays;
public class AsList {
public static void main(String args[]) {
// Before
List before = Arrays.asList(args);
// After
List after = Arrays.asList("One", "Two", "Three");
}
}
|
The two examples in Listing 3 won't necessarily produce the same results if the elements passed to the command-line are different, but it does show how the language-level changes of Tiger extend the functionality of the original asList() method of Arrays.
The complementary class of Arrays for the different collections is the Collections class. Again, the class isn't new, but the features of the class have been extended for 5.0. You now have 13 new methods:
checkedCollection()checkedSet()checkedSortedSet()checkedList()checkedMap()checkedSortedMap()emptySet()emptyList()emptyMap()reverseOrder()frequency()disjoint()addAll()
The six checked*() methods work similar to the six synchronized*() and unmodifiable*() methods. With the synchronized*() methods, you provide a collection to the method and are given back a synchronized, thread-safe version of that same collection. With unmodifiable*(), you get back a read-only view of the specified collection. The checked*() operation requires a second and possibly third argument, in addition to the collection (as shown in Listing 4), and returns a dynamically typesafe view of the collection:
Listing 4. Checked collections
public static <E> Collection<E> checkedCollection(
Collection<E> c, Class<E> type)
public static <E> Set<E> checkedSet(
Set<E> s, Class<E> type)
public static <E> SortedSet<E> checkedSortedSet(
SortedSet<E> s, Class<E> type)
public static <E> List<E> checkedList(
List<E> list, Class<E> type)
public static <K,V> Map<K,V> checkedMap(
Map<K,V> m, Class<K> keyType, Class<V> valueType)
public static <K,V> SortedMap<K,V> checkedSortedMap(
SortedMap<K,V> m, Class<K> keyType, Class<V> valueType)
|
With the Java 5.0 platform, you would think that because you declared a collection as generic (Collection<String> c = new HashSet<String>();), you wouldn't need run-time type checks, but if you were to pass your String version of HashSet to a utility method that only works with a non-generic Set, then that method could incorrectly add a non-String element to the set. Temporarily modifying the program to add the run-time check with Collection<String> c = Collections.checkedCollection(new HashSet<String>(), String.class); enables you to find the source of the problem quickly.
The three empty*() methods -- emptySet(), emptyList(), and emptyMap()-- generate empty immutable collections. While you can certainly create empty collections with method calls like new ArraySet(), that collection would then need to go through one of the unmodifiable*() methods to make sure the new collection was immutable. The empty methods provide empty, read-only collections in a more optimal way.
One of the bigger changes to the 5.0 Collections Framework is the addition of the new base interface Queue. While described in the "Concurrent Collections" tip (see Resources), the use of the interface is not limited to concurrency. In computer science, a queue data structure is your basic first-in, first-out (FIFO) structure. Items are added to the end and removed from the beginning. Not only do you add and remove elements, you can also look at the queue to see what is there. Listing 5 shows the five methods of the Queue interface:
Listing 5. The Queue interface
public boolean offer(Object element) public Object remove() public Object poll() public Object element() public Object peek() |
Keep in mind that Queue extends from the Collection interface, so implementations of the Queue interface also implement Collection. When using an implementation of Queue, you should try to limit yourself to methods of the interface. For instance, adding elements to a Queue could be done with the add() method of Collection, throwing an unchecked exception on failure. Instead, if a sized queue was full, the offer() method returns false, not causing you to deal with exceptions when full.
Several implementations of the Queue interface exist in the java.util.concurrent package, but not all of them. The LinkedList class has been retrofitted with the Queue interface with JDK 5.0 and the PriorityQueue has been added with JDK 5.0. The remaining implementations -- ArrayBlockingQueue, ConcurrentLinkedQueue, DelayQueue, LinkedBlockingQueue, PriorityBlockingQueue, and SynchronousQueue -- are all part of the java.util.concurrent package.
Because LinkedList isn't new, let's look at the new PriorityQueue class. As shown in Listing 6, you can create one in six ways. When a Comparator isn't available, the natural ordering of the elements is used to determine priority. If the elements don't implement the Comparable interface, then that is a run-time error:
Listing 6. PriorityQueue constructors
PriorityQueue() PriorityQueue(Collection<? extends E> c) PriorityQueue(int initialCapacity) PriorityQueue(int initialCapacity, Comparator<? super E> comparator) PriorityQueue(PriorityQueue<? extends E> c) PriorityQueue(SortedSet<? extends E> c) |
To demonstrate the use of PriorityQueue, the program in Listing 7 adds all the command line elements and processes them in alphabetical order. Had the queue structure been a LinkedList, the order would have been the more typical FIFO order, but a PriorityQueue relies on priorities to order elements:
Listing 7. PriorityQueue usage
import java.util.*;
import java.util.concurrent.*;
public class Priority {
public static void main(String args[]) {
Queue<String> queue =
new PriorityQueue<String>(Arrays.asList(args));
String element;
while ((element = queue.poll()) != null) {
System.out.println(element);
}
}
}
|
Listing 8 shows the output of running the program with a command line of one two three four:
Listing 8. Results from Listing 7
>java Priority one two three four four one three two |
One thing to mention about the new Queue interface, with relation to the Collections class: Methods checkedQueue(), emptyQueue(), synchronizedQueue(), and unmodifiableQueue() are all missing from the Collections class. According to bug reports, all cases but checkedQueue() were done purposely. For synchronizedQueue(), the concurrent collections are better alternatives then a mere wrapper. The others were deemed unnecessary. Perhaps checkedQueue() (and checkedBlockingQueue()) will be added with the Tiger/6.0 release.
| Description | Name | Size | Download method |
|---|---|---|---|
| Sample code | j-tiger07195-source.zip | 1 KB | HTTP |
Information about download methods
- Download J2SE 5.0 from the Sun Developer Network.
- The June 2004 Taming Tiger installment "Concurrent collections" explores blocking queues and concurrent maps. Read all John Zukowski's tips for making the most of the current release of the Java platform the the main Taming Tiger series page.
- Brian Goetz's "Introduction to generic types in JDK 5.0" (developerWorks, December 2004) introduces you to language changes to support generics.
- The article "Generics gotchas" (developerWorks, January 2005) by Brian Goetz explains some of the more confusing aspects of using generics.
- Read the
java.utilpackage javadoc. - Read about Lang and Util Enhancements in Sun's J2SE 5.0 documentation.
- To learn more about Java programming, visit the developerWorks Java
zone. You'll find technical documentation, how-to articles,
education, downloads, product information, and more.
- Visit the New to Java technology site for the latest resources to help you get started with Java programming.
- Get involved in the developerWorks community by participating in
developerWorks
blogs.

John Zukowski conducts strategic Java consulting with JZ Ventures, Inc. and is working with SavaJe Technologies to develop a next-generation mobile phone platform. His latest books are The Definitive Guide to Java Swing, Third Edition (Apress, June 2005) and Mastering Java 2, J2SE 1.4 (Sybex, April 2002).



