EnumMap or HashMap if lookup key is a String
Asked Answered
T

4

29

I'm trying to weigh the pros and cons of using an EnumMap over a HashMap. Since, I will always be looking up using a String, it seems that a HashMap with a String key would be the right choice. However, an EnumMap seems like better design because it conveys my intent to restrict keys to a specific enumeration. Thoughts?

Here is a make-believe example, showing how I will be using the Map:

enum AnimalType { CAT, DOG }
interface Animal {}
class Cat implements Animal {}
class Dog implements Animal {}

public class AnimalFactory {

    private static final Map<AnimalType, Animal> enumMap 
            = new EnumMap<AnimalType, Animal>(AnimalType.class);
    // versus
    private static final Map<String, Animal> stringMap 
            = new HashMap<String, Animal>();

    static {
        enumMap.put(AnimalType.CAT, new Cat());
        enumMap.put(AnimalType.DOG, new Dog());
        stringMap.put("CAT", new Cat());
        stringMap.put("DOG", new Dog());
    }
    public static Animal create(String type) {
        Animal result = enumMap.get(AnimalType.valueOf(type));
        Animal result2 = stringMap.get(type);
        return result;
    }
}

Assume that the AnimalType enum and map will ONLY be used by the AnimalFactory to create animals and nowhere else.

Which Map should I use?

Thurlow answered 4/5, 2012 at 8:24 Comment(0)
B
12

If all valid keys can be enumerated, I would use that as it ensures you are always working with a valid value.

It can also avoid confusion as String can be used for lots of things and is easy to turn an "Animal" string into a string used for something else. As enum types are not interchangable with other types in general (unless you use a common interface), there is less chance of error in coding.

Birth answered 4/5, 2012 at 8:27 Comment(2)
Given that the Animal enum and map will ONLY be used by the AnimalFactory to create animals and no where else, do you still think an EnumMap is better? What do you think about the cost of converting a string to an enum before the lookup when using an EnumMap?Thurlow
Its about the same as the cost of looking up the String in the Map itself. There is no doubling up in the EnumMap as its effectively a wrapper for an array. ;)Birth
O
2

If the set of possible keys is finite and known in advance (as your example/question suggest), then the enum is a perfect representation of this. As other said, the use of an enum ensures that no mistake will be made in the use of the key.

Furthermore, this implementation of Map is quite optimized, as the range of the keys is known in advance (as far as I knwow, the EnumMap uses an array of length numberOfEnums internally, indexed by the enum's ordinal).

So I would also recommend EnumMap.

Two (little) things to keep in mind though :

  • you won't be able to add specialized cases by inheritance (you cannot extend an enum, so no Maps of ANIMALS with specialized Maps of MAMMALS on the side for example)
  • when adding a member to your enum, if you add it "in the middle" of the other members you change the ordinals. As this info can be used by the EnumMap, this can prove problematic if you reload an EnumMap constructed from the old version of the enum (for example using Serialization)
Oconnell answered 4/5, 2012 at 9:12 Comment(1)
as you say, an EnumMap is faster, but there is the additional cost of converting the input string into an enum before performing the lookup.Thurlow
P
1

Key Of Map should be unmodifiable and Unique and this can be guranteed using Enum.

Also managing it would be easier and less error prone as compare to managing String.

So Go For EnumMap.

Also as we have advanced enums, we can attach many other information and operations with the keys itself.

enum AnimalType {

  Dog("I am dog and I hate cats", true), 
  CAT("I am cat and I love to eat rats", true),
  RAT("I am a mouse and I love tearing human cloths apart", false) ;

  private final String description;
  private final boolean isHelpFullToHuman;

  private AnimalType(String description , boolean isHelpFullToHuman) {
     this.description = description;
     this.isHelpFullToHuman = isHelpFullToHuman;
  }

  public boolean isHelpFullToHuman() {
     return isHelpFullToHuman;
  }

  public String getDescription() {
     return description;
  }

}
Pappy answered 4/5, 2012 at 8:29 Comment(1)
not listed keys will not be accepted in case of enums and managing string keys constants in some class will be a headache.though less error prone word in my comment wasn't the central focused element :)Pappy
B
0

First of all, all you keys are final immutable. You should definitely use EnumMap.

This is like Hash in Ruby:

options = { :font_size => 10, :font_family => "Arial" }

:font_size is the symbol in Ruby, the final static counterpart in Java.

Bradfield answered 2/7, 2013 at 13:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.