☕ 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

Collections Overview
The Java Collections Framework (JCF) is a unified architecture for representing and manipulating groups of objects. It provides a set of interfaces that define the operations a collection must support, a set of abstract classes that provide partial implementations, and a set of concrete implementations optimised for different use cases. Every Java developer uses collections daily — lists for sequences, sets for uniqueness, maps for key-value pairs, and queues for ordering — and choosing the right implementation for the right use case is one of the most fundamental practical skills in Java.
Iterable
Iterable<E> is the root interface of the Java Collections hierarchy. Any class that implements Iterable can be used in a for-each loop. It declares a single abstract method: iterator(), which returns an Iterator<E> that the for-each loop uses to traverse the elements. Implementing Iterable is all that is required to make a custom data structure work with Java's enhanced for loop, the Stream API, and any method that accepts an Iterable.
Collection Interface
Collection<E> is the root interface of the main collection hierarchy, extending Iterable<E>. It defines the common operations that all collection types must support: adding elements, removing elements, checking containment, querying size, clearing, converting to an array, and bulk operations. List, Set, and Queue all extend Collection. Map does not extend Collection because a map operates on key-value pairs rather than individual elements.
ArrayList
ArrayList is a resizable-array implementation of the List interface. It is the most commonly used collection in Java, providing dynamic sizing on top of a standard array. Elements are stored in contiguous memory, enabling O(1) random access by index. When the internal array is full, ArrayList automatically allocates a larger array and copies all elements. ArrayList is not thread-safe and preserves insertion order, allowing duplicates.