The Java TM Tutorial

| Start of Tutorial > Start of Trail | Search

Trail: Collections

## Lesson: Wrapper Implementations

> 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.

### Synchronization Wrappers

> 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:

> >     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);

> 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:

> >     List list = Collections.synchronizedList(new ArrayList());

> A collection created in this fashion is every bit as thread-safe as a "normally" synchronized collection like a Vector.

> 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:

> >     Collection c = Collections.synchronizedCollection(myCollection);
> >     synchronized(c) {
> >         Iterator i = c.iterator(); // Must be in synchronized block!
> >         while (i.hasNext())
> >             foo(i.next());
> >     }

> Failure to follow this advice may result in non-deterministic behavior.

> The idiom for iterating over a `Collection`-view of a synchronized `Map` is similar, but with one wrinkle. It is imperative that the user manually synchronize on the synchronized `Map` when iterating over any of its `Collection`-views, rather than synchronizing on the `Collection`-view itself:

> >     Map 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());
> >     }

> 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 the `List` example above, one cannot call `ArrayList`'s `ensureCapacity` operation on the wrapped `ArrayList`.

### Unmodifiable Wrappers

> 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 an `UnsupportedOperationException`. The unmodifiable wrappers have two main uses:
> - 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.
> Like the synchronization wrappers, there is one static factory method for each of the six core collection interfaces:

> >     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