In the previous post we saw how EnumSet is an optimized collection built for use with Enums. While I genuinely struggled to come up with scenarios where I could need a set of enums, I have often come across and used Enums as keys in my maps. Java has come up with an optimized collection for the above use case too - EnumMap.
The maps that we use are hash based implementations. Adding a key value pair involves
These two features of an enum makes it possible to implement an array based fixed size map for it.
Consider the contains method for EnumMap:
The maps that we use are hash based implementations. Adding a key value pair involves
- computing a hash for the key,
- locating a bucket for the hash and
- then adding the key-value pair to the bucket.
These two features of an enum makes it possible to implement an array based fixed size map for it.
public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements java.io.Serializable, Cloneable { /** * The <tt>Class</tt> object for the enum type of all the keys of this map. */ private final Class<K> keyType; /** * All of the values comprising K. (Cached for performance.) */ private transient K[] keyUniverse; /** * Array representation of this map. The ith element is the value * to which universe[i] is currently mapped, or null if it isn't * mapped to anything, or NULL if it's mapped to null. */ private transient Object[] vals; //remaining code... }As seen here, the EnumMap includes two fixed size arrays - an array of all possible Enum keys and an array of enum values.
Consider the contains method for EnumMap:
public boolean containsKey(Object key) { return isValidKey(key) && vals[((Enum)key).ordinal()] != null; }For any key, its ordinal acts as an index into the value array. If there is a value at this location it is returned, else null is returned. Thats it. No hashing, no buckets. This kind of implementation is what makes it both fast and compact.
public boolean containsValue(Object value) { value = maskNull(value); for (Object val : vals) if (value.equals(val)) return true; return false; }The containsValue method simply runs a single for loop over the array looking for the passed object. In the hash based implementations, we have a double for loop - one for the buckets and the other for the entries inside the bucket to locate the value.
public V get(Object key) { return (isValidKey(key) ? unmaskNull(vals[((Enum)key).ordinal()]) : null); } public V put(K key, V value) { typeCheck(key); int index = ((Enum)key).ordinal(); Object oldValue = vals[index]; vals[index] = maskNull(value); if (oldValue == null) size++; return unmaskNull(oldValue); }The get and put methods as seen simply access the location in our value array corresponding to ordinal associated with the passed key. Fast. And efficient. EnumMaps are something that we should use when working with enums as keys for our maps.
public static void main(String[] args) { EnumMap<Rating, String> enumMap = new EnumMap<Rating, String>(Rating.class); System.out.println("is BAD key present ? " + enumMap.containsKey(Rating.BAD)); enumMap.put(Rating.BAD, Rating.BAD.name()); System.out.println("Is BAD key present ? " + enumMap.containsKey(Rating.BAD)); System.out.println("Is object for BAD key present ? " + enumMap.containsValue(Rating.BAD.name())); System.out.println("Is object for BAD key present ? " + enumMap.get(Rating.BAD)); }The output is :
is BAD key present ? false Is BAD key present ? true Is object for BAD key present ? true Is object for BAD key present ? BAD
No comments:
Post a Comment