Feb 15
Eric Redmond in his post A Safe HashMap for Java describes a SafeHashMap
Here’s a suggestion to extend the capabilities of the same. Lets look at the code.
Define an interface to create an instance.
First, I create a new interface (We’ll get to know why very soon).
public interface InstanceProvider<K,V>
{
public V createNew(K k);
}
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<K, V> extends HashMap<K, V>
{
// Here's where we cache away the provider
private InstanceProvider<K,V> provider;
// Note that the provider is now
// passed to all the constructors
public SafeMap(
int initialCapacity,
float loadFactor,
InstanceProvider<K,V> provider)
{
super(initialCapacity,loadFactor);
this.provider = provider;
}
public SafeMap(
int initialCapacity,
InstanceProvider<K,V> provider)
{
this.provider = provider;
}
public SafeMap(
InstanceProvider<K,V> provider)
{
this.provider = provider;
}
public SafeMap(
Map<? extends K, ? extends V> m,
InstanceProvider<K,V> 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;
}
}
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<String, List<String>> myMap =
new SafeMap<String, List<String>>(
new InstanceProvider<String, List<String>>()
{
public List<String> createNew(String string)
{
List<String> list = new ArrayList<String>();
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<String, String[]> myMap =
new SafeMap<String, String[]>(
new InstanceProvider<String, String[]>()
{
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).
close Reblog this comment
![[image]](http://mowser.com/img?url=http%3A%2F%2Fmedia.disqus.com%2Fimages%2Fembed%2Fservices%2Fblog.png)
![[image]](http://mowser.com/img?url=http%3A%2F%2Fmedia.disqus.com%2Fimages%2Fembed%2Fservices%2Ffacebook.png)
![[image]](http://mowser.com/img?url=http%3A%2F%2Fmedia.disqus.com%2Fimages%2Fembed%2Fservices%2Flinkedin.png)
![[image]](http://mowser.com/img?url=http%3A%2F%2Fmedia.disqus.com%2Fimages%2Fembed%2Fservices%2Ftwitter.png)
![[image]](http://mowser.com/img?url=http%3A%2F%2Fmedia.disqus.com%2Fimages%2Fembed%2Fservices%2Fdelicious.png)
![[image]](http://mowser.com/img?url=http%3A%2F%2Fmedia.disqus.com%2Fimages%2Fembed%2Fservices%2Fflickr.png)
![[image]](http://mowser.com/img?url=http%3A%2F%2Fmedia.disqus.com%2Fimages%2Fembed%2Fservices%2Ftumblr.png)




Thanks. Your comment is awaiting approval by a moderator.
Do you already have an account? Log in and claim this comment.
Trackbacks