3 wrapper families for Collections Class

1. Introduction

Collections class is a static utility class which contains tons of methods which provides access to much needed functionality. This functionalities include polymorphic algorithms, static factories and wrapper methods.

Static Factories has been discussed here. Static factory methods are incredibly simple to use and provides much needed immutability to mutable collections.

In this article, I will talk about wrapper methods of Collections class which are implemented as protection proxies. A protection proxy is a variant of proxy pattern which controls access to real subject. Protection proxy can be created in different ways for example implementing same interface as original class and use the object of proxy to access the real subject.

In Java, proxy is implemented as inner class, which implements the required interface and provides a controlled access to elements of Collections. The controlled access is determined by the type of wrapper i.e. for synchronized collection the proxy controls the mode of accessing elements based on synchronization which means only one thread can access the collection. In case of unmodifiable and checked collections, if the client breaks the contract of proxy, the proxy will throw an exception.

2. Content

3 wrapper families for Collections class are as below:

  1. Synchronized Collections
  2. Unmodifiable Collections
  3. Checked Collections

3. Synchronized Collections

Most of classes in Collections framework are not designed as thread safe because of additional headache of synchronization which may or may not be used. But when the need arise to use a collection in multi-threaded environment we can use this synchronized wrappers of our collections.

Below are synchronized methods of Collections class. The method suite comprises of all the important interfaces of Java Collection Framework.

public static <T> 
	Collection<T> synchronizedCollection(Collection<T> c)

public static <T> 
	List<T> synchronizedList(List<T> list)
 
public static <T> 
	Set<T> synchronizedSet(Set<T> s)
public static <T> 
	SortedSet<T> synchronizedSortedSet(SortedSet<T> s)
public static <T> 
	NavigableSet<T> synchronizedNavigableSet(NavigableSet<T> s)
 
public static <K,V> 
	Map<K,V> synchronizedMap(Map<K,V> m)
public static <K,V> 
	SortedMap<K,V> synchronizedSortedMap(SortedMap<K,V> m)
public static <K,V> 
	NavigableMap<K,V> synchronizedNavigableMap(NavigableMap<K,V> m)

This static wrapper methods returns a synchronized view of specified collection. The synchronization provided by this wrapper methods is coarse grained and if an application makes heavy usage of this methods then the concurrency can be reduced by considerable amount.

The operations performed on the synchronized collections are atomic but there is a restriction in terms of iteration. Iteration must be manually synchronized. The rule also applies to methods stream(), spliterator() and parallelStream(). For List<> implementations we need to manually synchronize listIterator() method too. If we don’t manually synchronize iteration, at best wrapper method will throw ConcurrentModificationException.

Examples:

Synchronize a Collection

Collection<String> sync = Collections.synchronizedCollection(coll);

Synchronize and iterate List

List<String> names = new ArrayList<String>()
names.add("Jon");
names.add("Arya");
 
List<String> syncList = Collections.synchronizedList(names);
 
synchronized (syncList) {
    Iterator<String> i = syncList.iterator();
    while (i.hasNext()) doSomeThing(i.next());
}

Synchronize and iterate Set

Set<String> syncSet = 
	Collections.synchronizedSet(new HashSet<String>());

SortedSet<String> syncSortedSet = 
        Collections.synchronizedSortedSet(new TreeSet<String>());

NavigableSet<String> syncNavigableSet = 
        Collections.synchronizedNavigableSet(new TreeSet<String>());
 
synchronized (syncSet) {
	Iterator<String> i = syncSet.iterator();
	while (i.hasNext()) doSomeThing(i.next());
}

Synchronize and traverse Map

Map<String, String> syncMap = 
	Collections.synchronizedMap(
								new HashMap<String, String>());

SortedMap<String, String> syncSortedMap = 
	Collections.synchronizedSortedMap(
									new TreeMap<String, String>());

NavigableMap<String, String> syncNavigableMap = 
	Collections.synchronizedNavigableMap(
									new TreeMap<String, String>());

// Needn't be in synchronized block
Set<String> keySet = syncMap.keySet(); 
 
synchronized (syncMap) { // Synchronizing on map, not keySet.
	// Iterator must be in synchronized block
	Iterator<String> i = keySet.iterator(); 
	while (i.hasNext()) doSomeThing(i.next());
}

4. Unmodifiable Collections

Unmodifiable Collections are those whose structure cannot be changed. Any structure change will throw UnsupportedOperationException. This collections are extremely important when we return a Collection as a result. Read about why we should return unmodifiable Collections here.

List<String> filter(List<String> list, Predicate<String> pred) {
	List<String> result = new ArrayList<String>();
	for (String str : list) {
		if(pred.test(str)) {
			result.add(str);
		}
	}
	return result;
}

The returned result is a mutable collection and the result can be corrupted by the client. We can replace the result as Collections.unmodifiableList(result) and now the client gets view access to result.

public static <T> 
	Collection<T> unmodifiableCollection(Collection<? extends T> c)
 
public static <T> 
	List<T> unmodifiableList(List<? extends T> list)
 
public static <T> 
	Set<T> unmodifiableSet(Set<? extends T> s)
public static <T> 
	SortedSet<T> unmodifiableSortedSet(SortedSet<T> s)
public static <T> 
	NavigableSet<T> unmodifiableNavigableSet(NavigableSet<T> s)
 
public static <K,V> 
	Map<K,V> unmodifiableMap(Map<? extends K, ? extends V> m)
public static <K,V> 
	SortedMap<K,V> unmodifiableSortedMap(
									SortedMap<K, ? extends V> m)
public static <K,V> 
	NavigableMap<K,V> unmodifiableNavigableMap(
									NavigableMap<K, ? extends V> m)

Using unmodifiable collection methods.

Collection<String> unmodifColl 
	= Collections.unmodifiableCollection(mutableColl);
 
List<String> unmodifList 
	= Collections.unmodifiableList(mutableList);
 
Set<String> unmodifSet 
	= Collections.unmodifiableSet(mutableSet);
 
SortedSet<String> unmodifSortedSet 
	= Collections.unmodifiableSortedSet(mutableSortedSet);
 
NavigableSet<String> unmodifNavigableSet 
	= Collections.unmodifiableNavigableSet(mutableNavigableSet);
     
Map<String, String> unmodifMap 
	= Collections.unmodifiableMap(mutableMap);
 
SortedMap<String, String> unmodifSortedMap 
	= Collections.unmodifiableSortedMap(mutableSortedMap);
 
NavigableMap<String, String> unmodifNavigableMap 
	= Collections.unmodifiableNavigableMap(mutableNavigableMap);

5. Checked Collections

I believe it would be better if I talk about the Checked Collections in a different article. I will edit this page with the link to Checked Collections article once I write it.

6. Conclusion

Collections class is very helpful as we get ready made wrapper families to work on. This article talks about usage of those families and advantages of using them.

Leave a Reply

Your email address will not be published. Required fields are marked *