Eric Redmond in his post “A Safe HashMap for Java”:http://www.coderoshi.com/2008/02/safe-hashmap-for-java.html describes a SafeHashMap
Here’s a suggestion to extend the capabilities of the same. Lets look at the code.
h3. Define an interface to create an instance.
First, I create a new interface (We’ll get to know why very soon).
public interface InstanceProvider{ public V createNew(K k); }
h3. The SafeMap class itself
Here’s the proposed class. The main distinction is that instead of caching an instance and using the clone method to clone, this implementation stores away a reference to the instance provider and triggers it when required.
public class SafeMapextends HashMap { // Here's where we cache away the provider private InstanceProvider provider; // Note that the provider is now // passed to all the constructors public SafeMap( int initialCapacity, float loadFactor, InstanceProvider provider) { super(initialCapacity,loadFactor); this.provider = provider; } public SafeMap( int initialCapacity, InstanceProvider provider) { this.provider = provider; } public SafeMap( InstanceProvider provider) { this.provider = provider; } public SafeMap( Map extends K, ? extends V> m, InstanceProvider provider) { super(m); this.provider = provider; } @Override @SuppressWarnings("unchecked") public V get(Object key) { V value = super.get(key); if (value == null) { // use the provider here value = provider.createNew( (K) key); } return value; } }
h3. Test Case demonstrating usage.
public class TestSafeMap
{
@Test
public void testGetObject()
{
// Am using an anonymous class here.
// If additional parameters are required
// to be passed to constructor, one could
// create an abstract class with the constructor
// and pass the necessary arguments
Map> myMap =
new SafeMap>(
new InstanceProvider>()
{
public List createNew(String string)
{
List list = new ArrayList();
list.add(string);
return list;
}
}
);
String key = "hello world";
assertEquals(
"List size should've been one",
1,
myMap.get(key).size());
assertEquals(
"The only element in the list should've been : " + key,
key,
myMap.get(key).get(0));
}
@Test
public void testArrayInstantiation()
{
Map myMap =
new SafeMap(
new InstanceProvider()
{
public String[] createNew(
String string)
{
return new String[] {string};
}
}
);
String key = "hello world";
assertEquals(
"List size should've been one",
1,
myMap.get(key).length);
assertEquals(
"The only element in the list should've been : " + key,
key,
myMap.get(key)[0]);
}
}
This way I get a finer level of control on the instantiation of the default instance. There are multiple reasons why one might want that such as :
* The default instance needs to be configured in some way depending upon the key value (shown in the example above)
* The default instance needs to be configured based on some constructor parameters passed to it. In this case create an abstract class which implements the interface, declare a constructor with the necessary arguments, use the abstract class during instantiation, and allow the createNew method to behave appropriately based on the values of the arguments.
* There are some situations such as where the type above is a String[] where the clone method does not work (as in the second test case above). Perhaps the code to conduct the cloning could be modified above to create an array, but I am not too sure (since I’ve often faced difficulties working with instantiation of array types when using generics).
No related posts.


[...] responded to my earlier post An even more capable SafeHashMap with It is safer not to invent safe hash map / Java. While he does make some valid points I did [...]