Start of Tutorial > Start of Trail | Search |
Wrapper implementations are implementations that delegate all of their real work to a specified collection, but add some extra functionality on top of what this collection offers. For design patterns fans, this is an example of the decorator pattern. While it may seem a bit exotic, it's really pretty straightforward.These implementations are anonymous: rather than providing a public class, the JDK provides a static factory method. All of these implementations are found in the
Collections
API which consists solely of static methods.
The synchronization wrappers add automatic synchronization (thread-safety) to an arbitrary collection. There is one static factory method for each of the six core collection interfaces:Each of these methods returns a synchronized (thread-safe) Collection backed by the specified collection. In order to guarantee serial access, it is critical that all access to the backing collection is accomplished through the returned collection. The easy way to guarantee this is to not to keep a reference to the backing collection. Creating the synchronized collection like this does the trick:public static Collection synchronizedCollection(Collection c); public static Set synchronizedSet(Set s); public static List synchronizedList(List list); public static Map synchronizedMap(Map m); public static SortedSet synchronizedSortedSet(SortedSet s); public static SortedMap synchronizedSortedMap(SortedMap m);A collection created in this fashion is every bit as thread-safe as a "normally" synchronized collection like a Vector.List list = Collections.synchronizedList(new ArrayList());In the face of concurrent access, it is imperative that the user manually synchronize on the returned collection when iterating over it. This is because iteration is accomplished via multiple calls into the collection, which must be composed into a single atomic operation. The idiom to iterate over a wrapper-synchronized collection is:
Failure to follow this advice may result in non-deterministic behavior.Collection c = Collections.synchronizedCollection(myCollection); synchronized(c) { Iterator i = c.iterator(); // Must be in synchronized block! while (i.hasNext()) foo(i.next()); }The idiom for iterating over a
Collection
-view of a synchronizedMap
is similar, but with one wrinkle. It is imperative that the user manually synchronize on the synchronizedMap
when iterating over any of itsCollection
-views, rather than synchronizing on theCollection
-view itself:One minor downside of the wrapper implementation approach is that you do not have the ability to execute any non-interface operations of a wrapped implementation. So, for instance, in theMap m = Collections.synchronizedMap(new HashMap()); ... Set s = m.keySet(); // Needn't be in synchronized block ... synchronized(m) { // Synchronizing on m, not s! Iterator i = s.iterator(); // Must be in synchronized block while (i.hasNext()) foo(i.next()); }List
example above, one cannot callArrayList
'sensureCapacity
operation on the wrappedArrayList
.
The unmodifiable wrappers are conceptually similar to the synchronization wrappers, but simpler. Rather than adding functionality to the wrapped collection, they take it away. In particular, they take away the ability to modify the collection, by intercepting all of the operations that would modify the collection, and throwing anUnsupportedOperationException
. The unmodifiable wrappers have two main uses:Like the synchronization wrappers, there is one static factory method for each of the six core collection interfaces:
- To make a collection immutable once it has been built. In this case, it's good practice not to maintain a reference to the backing collection. This absolutely guarantees immutability.
- To allow "second-class citizens" read-only access to your data structures. You keep a reference to the backing collection, but hand out a reference to the wrapper. In this way, the second-class citizens can look but not touch, while you maintain full access.
public static Collection unmodifiableCollection(Collection c); public static Set unmodifiableSet(Set s); public static List unmodifiableList(List list); public static Map unmodifiableMap(Map m); public static SortedSet unmodifiableSortedSet(SortedSet s); public static SortedMap unmodifiableSortedMap(SortedMap m);
Start of Tutorial > Start of Trail | Search |