Contents

EnumMap stores values in an array indexed by each enum constant's ordinal. No hashing, no collisions — lookups and insertions are O(1) with minimal overhead.

import java.util.EnumMap; enum Day { MON, TUE, WED, THU, FRI, SAT, SUN } // Create and populate an EnumMap EnumMap<Day, Integer> workHours = new EnumMap<>(Day.class); workHours.put(Day.MON, 8); workHours.put(Day.TUE, 8); workHours.put(Day.WED, 8); workHours.put(Day.THU, 8); workHours.put(Day.FRI, 6); workHours.put(Day.SAT, 0); workHours.put(Day.SUN, 0); System.out.println(workHours.get(Day.FRI)); // 6 // EnumMap iteration is always in enum declaration order (MON → SUN) workHours.forEach((day, hours) -> System.out.printf("%s: %d hours%n", day, hours)); // Copy from a regular map Map<Day, String> dayNames = new HashMap<>(); dayNames.put(Day.MON, "Monday"); dayNames.put(Day.TUE, "Tuesday"); EnumMap<Day, String> enumDayNames = new EnumMap<>(dayNames); // copy constructor // putIfAbsent, computeIfAbsent — all Map methods work workHours.putIfAbsent(Day.SAT, 4); workHours.merge(Day.FRI, 2, Integer::sum); // FRI → 8 // getOrDefault int hours = workHours.getOrDefault(Day.SUN, 0);

EnumSet represents a set of enum values as a single long bit vector (for enums ≤ 64 values) or a long[] array. Set operations become bitwise operations — extremely fast.

import java.util.EnumSet; enum Permission { READ, WRITE, EXECUTE, DELETE, ADMIN } // Creating EnumSets EnumSet<Permission> empty = EnumSet.noneOf(Permission.class); EnumSet<Permission> all = EnumSet.allOf(Permission.class); EnumSet<Permission> readWrite = EnumSet.of(Permission.READ, Permission.WRITE); EnumSet<Permission> readOnly = EnumSet.of(Permission.READ); // range — all constants between two (by declaration order) EnumSet<Permission> firstThree = EnumSet.range(Permission.READ, Permission.EXECUTE); // {READ, WRITE, EXECUTE} // complementOf — all constants NOT in the given set EnumSet<Permission> notReadWrite = EnumSet.complementOf(readWrite); // {EXECUTE, DELETE, ADMIN} // copyOf — copy an existing EnumSet EnumSet<Permission> copy = EnumSet.copyOf(readWrite); // Standard Set operations — all very fast (bitwise internally) readWrite.add(Permission.EXECUTE); // add readWrite.remove(Permission.WRITE); // remove boolean canRead = readWrite.contains(Permission.READ); // true // Set algebra EnumSet<Permission> union = EnumSet.copyOf(readWrite); union.addAll(readOnly); // union EnumSet<Permission> intersection = EnumSet.copyOf(readWrite); intersection.retainAll(readOnly); // intersection EnumSet<Permission> difference = EnumSet.copyOf(readWrite); difference.removeAll(readOnly); // difference

EnumSet has no public constructor — instances are always created through static factory methods. The factories cover all common patterns: empty set, full set, explicit members, a contiguous range by declaration order, and the complement of an existing set:

enum Status { PENDING, PROCESSING, SHIPPED, DELIVERED, CANCELLED, RETURNED } // noneOf — empty set of the given enum type EnumSet<Status> noStatuses = EnumSet.noneOf(Status.class); // allOf — set containing all constants EnumSet<Status> allStatuses = EnumSet.allOf(Status.class); // of — 1 to 5 args, or varargs (6+ args use the varargs form) EnumSet<Status> active = EnumSet.of(Status.PENDING, Status.PROCESSING, Status.SHIPPED); EnumSet<Status> terminal = EnumSet.of(Status.DELIVERED, Status.CANCELLED, Status.RETURNED); // range — inclusive on both ends, uses enum declaration order EnumSet<Status> inFlight = EnumSet.range(Status.PROCESSING, Status.SHIPPED); // {PROCESSING, SHIPPED} // complementOf — everything else EnumSet<Status> notActive = EnumSet.complementOf(active); // {DELIVERED, CANCELLED, RETURNED} // Using as flags — idiomatic pattern boolean isTerminal(Status status) { return terminal.contains(status); } boolean isActive(Status status) { return active.contains(status); } // Method accepting Set<Status> — use EnumSet for the argument void processOrders(EnumSet<Status> statusFilter) { orders.stream() .filter(o -> statusFilter.contains(o.status())) .forEach(this::process); } // Call: processOrders(EnumSet.of(Status.PENDING, Status.PROCESSING));

EnumMap is faster than HashMap because it uses an ordinal-indexed array — no hashing, no collision resolution. EnumSet is even more dramatic: for enums with up to 64 constants the entire set is a single long, so operations like contains() reduce to a single bitwise test:

// EnumMap vs HashMap — benchmark illustration (not runnable) // EnumMap: no hash computation, no collision handling // HashMap: hash(key) → bucket lookup → possible chain/tree traversal // EnumSet vs HashSet — even bigger difference // EnumSet.contains(x): single bit test — O(1), ~1 CPU instruction // HashSet.contains(x): hash, bucket, equals — O(1) amortized but higher constant // Memory comparison for an enum with 8 constants, all in the collection: // EnumSet: 1 long (8 bytes) for the bit vector // HashSet: 8 Entry objects (~32+ bytes each) + array + header // EnumSet for flags — replaces int bitmasks cleanly: // Old (pre-Java-5 style, error-prone): // static final int READ = 1 << 0; // static final int WRITE = 1 << 1; // static final int EXECUTE = 1 << 2; // int perms = READ | WRITE; // if ((perms & READ) != 0) { ... } // Modern (type-safe, readable, same performance): EnumSet<Permission> perms = EnumSet.of(Permission.READ, Permission.WRITE); if (perms.contains(Permission.READ)) { /* ... */ } // Iteration order: // EnumMap — always enum declaration order (consistent, predictable) // EnumSet — always enum declaration order // HashMap — unspecified (insertion or hash order) // LinkedHashMap — insertion order // TreeMap — natural/comparator order

Three patterns appear repeatedly in production code: feature-flag configuration (EnumMap<Feature, Boolean>), strategy dispatch (EnumMap<Op, Function> replacing switch statements), and role-based access control (EnumMap<Role, EnumSet<Permission>>):

// Pattern 1: Configuration with EnumMap enum Feature { DARK_MODE, NOTIFICATIONS, AUTO_SAVE, SPELL_CHECK } class UserPreferences { private final EnumMap<Feature, Boolean> enabled = new EnumMap<>(Feature.class); public UserPreferences() { // Default all features to false for (Feature f : Feature.values()) enabled.put(f, false); } public void enable(Feature feature) { enabled.put(feature, true); } public void disable(Feature feature) { enabled.put(feature, false); } public boolean isEnabled(Feature f) { return enabled.getOrDefault(f, false); } public EnumSet<Feature> enabledFeatures() { return enabled.entrySet().stream() .filter(Map.Entry::getValue) .map(Map.Entry::getKey) .collect(Collectors.toCollection(() -> EnumSet.noneOf(Feature.class))); } } // Pattern 2: Strategy dispatch with EnumMap enum Operation { ADD, SUBTRACT, MULTIPLY, DIVIDE } EnumMap<Operation, IntBinaryOperator> ops = new EnumMap<>(Operation.class); ops.put(Operation.ADD, (a, b) -> a + b); ops.put(Operation.SUBTRACT, (a, b) -> a - b); ops.put(Operation.MULTIPLY, (a, b) -> a * b); ops.put(Operation.DIVIDE, (a, b) -> a / b); int calculate(Operation op, int a, int b) { return ops.get(op).applyAsInt(a, b); } // Pattern 3: Role-based access control with EnumSet enum Role { USER, MODERATOR, ADMIN } EnumMap<Role, EnumSet<Permission>> rolePermissions = new EnumMap<>(Role.class); rolePermissions.put(Role.USER, EnumSet.of(Permission.READ)); rolePermissions.put(Role.MODERATOR, EnumSet.of(Permission.READ, Permission.WRITE, Permission.DELETE)); rolePermissions.put(Role.ADMIN, EnumSet.allOf(Permission.class)); boolean hasPermission(Role role, Permission perm) { return rolePermissions.getOrDefault(role, EnumSet.noneOf(Permission.class)) .contains(perm); } Always use EnumMap and EnumSet when keys/elements are enum constants. They're never the wrong choice — they are type-safe, faster, and more memory-efficient than HashMap and HashSet for this use case.