Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Java EnumMap and EnumSet: finite-state storage

Last updated: 30 Sept 20264 min read
tutorial
IntermediateBy AITrove Editorial

EnumMap maps keys from one enum type to values, and EnumSet stores a set drawn from one enum type’s finite constants.

Java 8+. The program uses only JDK classes and runs without a framework.

Represent a finite domain directly

A fulfillment pipeline has a known set of stages. Using enum keys prevents misspelled string labels and avoids manually treating ordinal numbers as stable business identifiers. The sample counts jobs by stage and records which stages require manual review.

EnumMap iteration follows declaration order, which is useful for a fixed report layout. Missing keys and keys mapped to null still need a policy: get returning null does not alone distinguish them. This program uses positive integer counts and leaves absent stages unmapped, so getOrDefault can express a zero count for an untouched stage.

EnumSet is useful for combinations of allowed states or permissions inside trusted application logic. It is not a replacement for a persisted permission model. Adding or reordering enum constants should not change the meaning of a database value; persist a deliberate stable code rather than the ordinal.

Copying the set does not freeze the domain

EnumSet.copyOf with an EnumSet source preserves the element type even when the set is empty. Copying an arbitrary empty collection can fail because there is no element from which to infer the enum type. Use noneOf(Stage.class) when constructing an empty typed set.

The example keeps the map and set method-local. A shared map still needs synchronization or confinement, and publishing a mutable EnumSet gives callers an editing handle. Choose an unmodifiable wrapper or defensive copy at the boundary, then explain whether subsequent server changes should be visible to the caller.

Typed copies and empty inputs

EnumMap needs the key enum type to allocate its representation. A copy constructor receiving an empty ordinary Map cannot infer that type; provide the enum class explicitly, then putAll the input. EnumMap can store null values, so get alone cannot distinguish an absent mapping from a mapped null. Keep the value contract explicit.

EnumSet.noneOf also needs the enum class. A snapshot copied from an existing EnumSet retains its enum type even when it is empty, but copying an empty arbitrary collection cannot infer one. Neither structure is a concurrency boundary; a compact representation does not make several state changes atomic.

Working program

Java
import java.util.EnumMap;
import java.util.EnumSet;
public class FulfillmentStates {
    enum Stage { RECEIVED, PACKED, DISPATCHED }
    public static void main(String[] args) {
        EnumMap<Stage, Integer> counts = new EnumMap<>(Stage.class);
        for (Stage stage : new Stage[]{Stage.RECEIVED, Stage.PACKED, Stage.PACKED}) {
            counts.merge(stage, 1, Integer::sum);
        }
        EnumSet<Stage> review = EnumSet.of(Stage.RECEIVED, Stage.PACKED);
        System.out.println(counts);
        System.out.println("dispatched=" + counts.getOrDefault(Stage.DISPATCHED, 0));
        EnumSet<Stage> accepted = EnumSet.copyOf(review);
        accepted.remove(Stage.RECEIVED);
        System.out.println(accepted);
    }
}

Output

Output
{RECEIVED=1, PACKED=2}
dispatched=0
[PACKED]

Cost and failure boundaries

EnumMap is specialized for a fixed enum domain. Its storage relates to the number of declared constants, so a tiny populated map over a huge enum is not identical to a tiny generic hash map. The example has three constants and scans three submitted stages: O(r) updates for r records plus reporting work.

EnumSet uses a compact representation for finite membership. The exact representation can vary with the domain size and implementation; do not promise one machine word for an arbitrary enum. The defensive copy isolates subsequent membership edits, but copying a map does not recursively copy mutable mapped objects.

Common Mistakes

  • Do not persist ordinal() as a long-lived external identifier.
  • Do not infer an enum type from an arbitrary empty collection.
  • Do not mistake a typed finite domain for concurrency control.

Connect the contracts

Membership depends on the fixed constants described by enum state definitions.

Choose ownership for returned collection views separately from the enum-backed representation.

Continue with ownership and failure checks

Continue with Java EnumMap: declaration order and nullable values.

java
enum-collections
Storage details