Saturday, 20 July 2013

Java Concurrent Set and Lifo Queue - Problem with java.util.Collections factory methods

Today I got to know about java.util.Collections.newSetFromMap() method, added in Java 6 (aka Java 1.6), through [JavaSpecialists] newsletter.

Notwithstanding its utility, I was a little disturbed by its name. By and large, the naming in Java has been done considerable effort with focus on affordance. The semantics of the elements used to match quite closely with the names.

This one have too vague a name, and the other I found, asLifoQueue.

Apart from this, a much bigger problem these methods in API pose is the semantic override they provide on the returned interface. In newSetFromMap method a Set interface is returned but is heavily dependent on the underlying Map.

As a general rule of generalization is that one should not assume any specialization, for example, if a reference of type Map has ConcurrentHashMap object, the user should not assume that it is concurrent. The only way to use it concurrently should be to downcast it and then test it as ConcurrentHashMap.

Set<String> names = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>()
The above example creates an issue; how does a user of names verify if the set is concurrent or not. This problem is more than just an academic issue, if the scope of the object is more than a few lines of code, much real issue if this object is passed across methods.

Similarly, java.util.Collections.asLifoQueue returns a Queue Interface. The user of the returned reference cannot infer if the queue is LIFO.

It would have been much better if the API added additional wrapper instead of wrapper factory methods. The semantics of the operations of these collections could be appropriately documented where they could be useful for the consumer of the objects returned from these methods.
For the users, I would recommend NOT to use the methods directly, instead declare classes and use the wrapper methods in those classes.

So, to make a concurrent set use this:
public class ConcurrentSet<E> implements Set<E> {
 private Set<E> underlyingSet;
 public ConcurrentSet(){
  this(new ConcurrentHashMap<E, Boolean>());
 }
 public ConcurrentSet(ConcurrentHashMap<E,Boolean> concurrentHashMap){
  underlyingSet = Collections.newSetFromMap(concurrentHashMap);
 }
 // …
 // Delegate all methods of Set interface to underlyingSet,
 // you can use Eclipse->Source->Generate Delegate Methods.
}
Download Source Code for ConcurrentSet.

Similarly for a FIFOQueue, use this:

public class FIFOQueue<E> implements Queue<E> {
 private Queue<E> underlyingQueue;
 public FIFOQueue(){
  this(new LinkedList<E>());
 }
 public FIFOQueue(Deque<E> deque){
  this.underlyingQueue = Collections.asLifoQueue(deque);
 }
 // …
 // Delegate all methods of Queue interface to underlyingQueue,
 // you can use Eclipse->Source->Generate Delegate Methods.
}

Download Source Code for FIFIQueue.

Remember the best practice is to:Program for other programmers; any dummy can program for the computer.

No comments:

Post a Comment