☕ Java
EnumMap
EnumMap is a specialised Map implementation for use with enum keys. It stores values in an array indexed by the enum's ordinal — the position of the constant in the enum declaration. This gives EnumMap O(1) operations with extremely low constant overhead — it is the fastest Map for enum keys. EnumMap maintains keys in the natural order of the enum constants (declaration order). It does not permit null keys but permits null values.
EnumMap — Array-Based Implementation for Enum Keys
EnumMap's implementation is elegantly simple. Every enum has a known, finite set of constants with positions (ordinals) starting from 0. EnumMap uses a fixed-size Object[] where index equals the enum constant's ordinal. Putting the value for an enum key is array assignment: vals[key.ordinal()] = value. Getting a value is array access: vals[key.ordinal()]. Both are O(1) with a single array access — the absolute minimum for any map operation.
This is significantly faster than HashMap for enum keys. HashMap must compute a hash code, apply supplemental hashing, compute a bucket index, and potentially traverse a collision chain. EnumMap bypasses all of this — the ordinal is a direct array index computed in nanoseconds. No hash computation, no collision handling, no pointer chasing.
EnumMap also has lower memory overhead than HashMap. HashMap stores entries as Node objects with hash, key, value, and next fields. EnumMap stores only the value in the array — the key is implicit in the array position. This saves memory proportional to the number of entries.
The iteration order of EnumMap always follows the declaration order of the enum constants — the same as ordinal order. This is both predictable and useful: iterating over a Day → Task map always produces Monday through Sunday in order without any sorting.
Because EnumMap is typed at construction with the enum's Class object (which is used to determine the array size), an EnumMap<Day, String> can only hold Day keys. This is enforced at compile time. The Class is required by the constructor: new EnumMap<>(Day.class).
Java
// ── EnumMap — fastest map for enum keys: ─────────────────────────────
enum Day { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY }
// Constructor requires the enum Class:
EnumMap<Day, String> schedule = new EnumMap<>(Day.class);
schedule.put(Day.MONDAY, "Team standup at 9am");
schedule.put(Day.WEDNESDAY, "Sprint review at 2pm");
schedule.put(Day.FRIDAY, "Retrospective at 3pm");
// ── O(1) access via ordinal array: ───────────────────────────────────
System.out.println(schedule.get(Day.MONDAY)); // Team standup at 9am
System.out.println(schedule.get(Day.TUESDAY)); // null — not in map
// ── Iteration always in declaration order: ────────────────────────────
for (Map.Entry<Day, String> e : schedule.entrySet()) {
System.out.printf("%-10s → %s%n", e.getKey(), e.getValue());
}
// MONDAY → Team standup at 9am
// WEDNESDAY → Sprint review at 2pm
// FRIDAY → Retrospective at 3pm
// (In declaration order — Monday before Wednesday before Friday)
// ── Common pattern — map all enum values: ────────────────────────────
enum Status { PENDING, ACTIVE, INACTIVE, SUSPENDED }
EnumMap<Status, String> descriptions = new EnumMap<>(Status.class);
for (Status s : Status.values()) {
descriptions.put(s, s.name().charAt(0) +
s.name().substring(1).toLowerCase());
}
System.out.println(descriptions);
// {PENDING=Pending, ACTIVE=Active, INACTIVE=Inactive, SUSPENDED=Suspended}
// ── Performance comparison: ───────────────────────────────────────────
//
// EnumMap HashMap<Day,V>
// put() array write hash + bucket
// get() array read hash + bucket + chain
// memory 1 Object[] Node per entry
// iteration declaration ord arbitrary order
//
// EnumMap is 3-5x faster than HashMap for enum keys in microbenchmarks.
// ── Copy constructor from another map: ───────────────────────────────
Map<Day, String> sourceMap = Map.of(Day.MONDAY, "meeting");
EnumMap<Day, String> copy = new EnumMap<>(sourceMap);
// EnumMap from another EnumMap:
EnumMap<Day, String> copy2 = new EnumMap<>(schedule);EnumMap Practical Patterns
EnumMap is the right choice whenever the key space is an enum — which in well-designed code is common for states, modes, categories, directions, commands, and other fixed vocabularies. Several patterns recur in professional Java code.
The state machine transition table uses EnumMap<State, Map<Event, State>> to store what next state each event produces in each current state. Lookups are two EnumMap array accesses — extremely fast. The strategy map pattern uses EnumMap<Category, Handler> to dispatch to different processing strategies based on a category enum. The frequency counter uses EnumMap<Status, AtomicInteger> to count occurrences of each enum value concurrently.
A practical advantage of EnumMap over switch-based dispatch is extensibility. When a new enum constant is added, the switch must be found and updated (and the compiler can warn about missing cases); the EnumMap can be populated in a loop over values() and automatically handles any new constant. This is more resilient to enum evolution.
EnumSet (analogous to EnumMap but for sets) uses a long bitmask where each bit represents one enum constant. It is even faster than EnumMap for set operations and should be used when you need a set of enum constants rather than a map from enum constants to values.
Java
// ── State machine transition table: ──────────────────────────────────
enum OrderState { PENDING, CONFIRMED, SHIPPED, DELIVERED, CANCELLED }
enum OrderEvent { CONFIRM, SHIP, DELIVER, CANCEL }
// transitions[currentState][event] = nextState
EnumMap<OrderState, EnumMap<OrderEvent, OrderState>> transitions =
new EnumMap<>(OrderState.class);
transitions.put(OrderState.PENDING, new EnumMap<>(OrderEvent.class));
transitions.get(OrderState.PENDING).put(OrderEvent.CONFIRM, OrderState.CONFIRMED);
transitions.get(OrderState.PENDING).put(OrderEvent.CANCEL, OrderState.CANCELLED);
transitions.put(OrderState.CONFIRMED, new EnumMap<>(OrderEvent.class));
transitions.get(OrderState.CONFIRMED).put(OrderEvent.SHIP, OrderState.SHIPPED);
transitions.get(OrderState.CONFIRMED).put(OrderEvent.CANCEL, OrderState.CANCELLED);
transitions.put(OrderState.SHIPPED, new EnumMap<>(OrderEvent.class));
transitions.get(OrderState.SHIPPED).put(OrderEvent.DELIVER, OrderState.DELIVERED);
// Transition:
public OrderState transition(OrderState current, OrderEvent event) {
EnumMap<OrderEvent, OrderState> eventMap = transitions.get(current);
if (eventMap == null) throw new IllegalStateException(
"No transitions from " + current);
OrderState next = eventMap.get(event);
if (next == null) throw new IllegalStateException(
"Cannot " + event + " from state " + current);
return next;
}
// ── Strategy dispatch: ────────────────────────────────────────────────
enum FileType { CSV, JSON, XML, PARQUET }
@FunctionalInterface
interface FileParser { List<Record> parse(InputStream in); }
EnumMap<FileType, FileParser> parsers = new EnumMap<>(FileType.class);
parsers.put(FileType.CSV, in -> parseCsv(in));
parsers.put(FileType.JSON, in -> parseJson(in));
parsers.put(FileType.XML, in -> parseXml(in));
parsers.put(FileType.PARQUET, in -> parseParquet(in));
// Dispatch:
public List<Record> parse(FileType type, InputStream in) {
FileParser parser = parsers.get(type);
if (parser == null) throw new IllegalArgumentException(
"Unsupported file type: " + type);
return parser.parse(in);
}
// ── Counting with EnumMap: ────────────────────────────────────────────
enum Priority { LOW, MEDIUM, HIGH, CRITICAL }
EnumMap<Priority, Integer> taskCounts = new EnumMap<>(Priority.class);
for (Priority p : Priority.values()) taskCounts.put(p, 0); // initialise all
List<Task> tasks = getTasks();
for (Task t : tasks) {
taskCounts.merge(t.getPriority(), 1, Integer::sum);
}
System.out.println(taskCounts);
// {LOW=12, MEDIUM=8, HIGH=4, CRITICAL=1}Related Topics in Collections Framework
HashMap
HashMap is the most widely used Map implementation in Java. It stores key-value pairs in a hash table, providing average O(1) performance for get, put, remove, and containsKey operations. Keys are stored in no guaranteed order. HashMap permits one null key and multiple null values. It is not thread-safe. Understanding how HashMap uses hash codes, handles collisions, and resizes is foundational knowledge for writing efficient Java code.
LinkedHashMap
LinkedHashMap extends HashMap and maintains a doubly-linked list running through all its entries, preserving the order in which entries were inserted. Iteration over a LinkedHashMap always returns entries in insertion order. Optionally, it can be constructed in access-order mode where entries are ordered by most-recently accessed, making it the perfect foundation for implementing a Least Recently Used (LRU) cache. LinkedHashMap has slightly higher memory overhead than HashMap due to the extra linked list pointers.
TreeMap
TreeMap is a Red-Black tree implementation of the NavigableMap interface. It stores key-value pairs in sorted order — either the natural ordering of keys (requiring them to implement Comparable) or a custom Comparator provided at construction. All basic operations (get, put, remove, containsKey) are O(log n). TreeMap provides rich navigation operations: finding the closest key, extracting sub-maps, and headMap/tailMap views.
Hashtable
Hashtable is a legacy hash table implementation that predates the Collections Framework. Like Vector for lists, Hashtable was Java's original hash map from Java 1.0. It is thread-safe because all its methods are synchronised, but it does not permit null keys or null values. Hashtable has been retrofitted to implement the Map interface but should never be used in new code — HashMap is the non-concurrent replacement and ConcurrentHashMap is the concurrent replacement.