Thursday, November 13, 2008

Zero length Java array cache

Many times I find myself declaring private static final zero length array fields of some type or the other. Some times the zero length arrays for a same type are declared in more than one place. To avoid this, I came up with the simple zero length array cache backed by a WeakHashMap. The beauty of this is that it does not require a cast because it uses the generic method which uses the Class
as a type token.

import java.util.Map;
import java.util.WeakHashMap;

/**
* A simple cache using WeakHashMap of zero length arrays of a given class.
*
* @author schitale
*
*/
public class ZeroLength {
private static Map map = new WeakHashMap();

@SuppressWarnings("unchecked")
public static T[] array(Class c) {
T[] array = (T[]) map.get(c);
if (array == null) {
array = (T[]) java.lang.reflect.Array.newInstance(c, 0);
map.put(c, array);
}
return array;
}

public static void main(String[] args) {
System.out.println(array(String.class).getClass().getCanonicalName()
+ " of length " + array(String.class).length);
System.out.println(array(String[].class).getClass().getCanonicalName()
+ " of length " + array(String.class).length);
}
}
The output is:
java.lang.String[] of length 0
java.lang.String[][] of length 0

If you want to rely on the identity of the zero length array instance (e.g. == based comparison) then replace the WeakHashMap with HashMap. However, in that case you have to watch out for memory leaks through static fields of array component classes. You may want to add a method to remove the reference to the array from the HashMap.

Is there a better way to do this?

IMHO the java.lang.Class class should provide such factory method. This is along the lines of factory method:
public static final T  List T java.util.Collections.emptyList();


The implementation in java.lang.Class may look like:

private static T[] zeroLengthArray;

public static synchronized T[] emptyArray() {
if (zeroLengthArray == null) {
zeroLengthArray = (T[]) java.lang.reflect.Array.newInstance(this, 0);
}
return zeroLengthArray;
}

7 comments:

Wombat42 said...

While the code is interesting, I'm not sure when I would use this code. Could you give a more concrete example of when this would be used?

aberrant said...

I applaud your use of generics for the interface to your map, but a WeakHashMap is not a cache.

I've implemented caches using maps of SoftReferences and a ReferenceQueue.

You might want to read this:
http://www.codeinstructions.com/2008/09/weakhashmap-is-not-cache-understanding.html

Sandip Chitale said...
This comment has been removed by the author.
Sandip Chitale said...

@aberrant - you are right. WeakHashMap should not be thought of as a cache. Instead one could use HashMap and manage the it with references counted release of the cached instance.

JoDelight said...

It should be thread safe. E.g. using a HashMap from concurrent threads may leed to an endless loop in the internals of "put". So use a cache implementation based on ConcurrentHashMap or so..

Sandip Chitale said...

@wombat42

Concreate example is to avoid code like this:


List<String> strings = ....

:

String[] stringArray = strings.toArray(new String[0]);

// Could be replaced by:

String[] stringArray = strings.toArray(ZeroLength.array(String.class);

In fact the java.lang.Class<T> could provide such factory method which will avoid issues with memory leaks. This is allong the lines of factory methods in
java.util.Collections.emptyList()

Sandip Chitale said...

@jodelight

You are right the code needs to be thread safe.