An even more capable SafeHashMap

Posted by – February 15, 2008

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

1 Comment on An even more capable SafeHashMap

Closed

  1. [...] 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 [...]