The JavaTM Tutorial
Previous Page Lesson Contents Next Page Start of Tutorial > Start of Trail > Start of Lesson Search
Feedback Form

Trail: Collections
Lesson: Implementations

Wrapper Implementations

Wrapper implementations delegate all their real work to a specified collection but add extra functionality on top of what this collection offers. For design patterns fans, this is an example of the decorator pattern. Although it may seem a bit exotic, it's really pretty straightforward.

These implementations are anonymous: rather than providing a public class, the library provides a static factory method. All these implementations are found in the Collections (in the API reference documentation) class, which consists solely of static methods.

Synchronization Wrappers

The synchronization wrappers add automatic synchronization (thread-safety) to an arbitrary collection. Each of the six core collection interfaces ( Collection (in the API reference documentation), Set (in the API reference documentation), List (in the API reference documentation), Map (in the API reference documentation), SortedSet (in the API reference documentation), and SortedMap (in the API reference documentation)) has one static factory method:
public static <E>Collection<E>
    synchronizedCollection(Collection<E> c);
public static <E>Set<E> synchronizedSet(Set<E> s);
public static <E>List<E> synchronizedList(List<E> list);
public static <K, V>Map<K, V> synchronizedMap(Map<K, V> m);
public static <E>SortedSet<E>
    synchronizedSortedSet(SortedSet<E> s);
public static SortedMap<K, V>
    synchronizedSortedMap(SortedMap<K, V> m);
Each of these methods returns a synchronized (thread-safe) Collection backed by the specified collection. In order to guarantee serial access, all access to the backing collection must be accomplished through the returned collection. The easy way to guarantee this is not to keep a reference to the backing collection. Creating the synchronized collection like this does the trick:
List<Type> list =
    Collections.synchronizedList(new ArrayList<Type>());
A collection created in this fashion is every bit as thread-safe as a normally synchronized collection, such as a Vector (in the API reference documentation).

In the face of concurrent access, it is imperative that the user manually synchronize on the returned collection when iterating over it. The reason is that iteration is accomplished via multiple calls into the collection, which must be composed into a single atomic operation. Here is the idiom to iterate over a wrapper-synchronized collection:

Collection<Type> c =
    Collections.synchronizedCollection(myCollection);
synchronized(c) {
    for (Type e : c)
        foo(e);
}
If an explicit iterator is used, the iterator method must be called from within the synchronized block. Failure to follow this advice may result in nondeterministic behavior. The idiom for iterating over a Collection view of a synchronized Map is similar. It is imperative that the user synchronize on the synchronized Map when iterating over any of its Collection views rather than synchronizing on the Collection view itself, as shown in the following example:
Map<KeyType, ValType> m =
    Collections.synchronizedMap(new HashMap<KeyType, ValType>());
    ...
Set<KeyType> s = m.keySet();
    ...
synchronized(m) {  // Synchronizing on m, not s!
    while (KeyType k: s)
        foo(k);
}
One minor downside of using wrapper implementations is that you do not have the ability to execute any noninterface operations of a wrapped implementation. So, for instance, in the preceding List example, you cannot call ArrayList's ensureCapacity (in the API reference documentation) operation on the wrapped ArrayList.

Unmodifiable Wrappers

Unlike the synchronization wrappers, which add functionality to the wrapped collection, the unmodifiable wrappers take functionality away. In particular, they take away the ability to modify the collection, by intercepting all the operations that would modify the collection and throwing an UnsupportedOperationException. The unmodifiable wrappers have two main uses: Like the synchronization wrappers, each of the six core collection interfaces has one static factory method.
public static Collection<T>
    unmodifiableCollection(Collection<? extends T> c);
public static Set<T> unmodifiableSet(Set<? extends T> s);
public static
    List<T> unmodifiableList(List<? extends T> list);
public static Map<K, V> 
    unmodifiableMap(Map<? extends K, ? extends V> m);
public static SortedSet<T> 
    unmodifiableSortedSet(SortedSet<? extends T> s);
public static SortedMap<K, V>
    unmodifiableSortedMap(SortedMap<K, ? extends V> m);

Checked Interface Wrappers

The Collections.checked Interface wrappers are provided for use with generic collections. These implementations return a dynamically typesafe view of the specified collection, which throws a ClassCastException if a client attempts to add an element of the wrong type. The generics mechanism in the language provides compile-time (static) type checking, but it is possible to defeat this mechanism. Dynamically typesafe views eliminate this possibility entirely.

Previous Page Lesson Contents Next Page Start of Tutorial > Start of Trail > Start of Lesson Search
Feedback Form

Copyright 1995-2005 Sun Microsystems, Inc. All rights reserved.