The Collections Framework

See how List, Set, Queue and Map fit together in java.util, why you declare the interface type, and the habits that keep shared collections safe.

SyntaxList<Order> orders = new ArrayList<>(); Set<String> roles = new HashSet<>(); Map<String, User> byId = new HashMap<>(); Deque<Job> jobs = new ArrayDeque<>();

The collections framework is the set of interfaces and classes in java.util that hold groups of objects. Almost every back-end method touches one: a repository returns a List of rows, a cache is a Map, a set of granted roles is a Set, and a JSON array or object becomes a List or Map when a library binds it.

Interfaces and implementations

The framework separates what a collection does from how it stores data:

  • Collection is the root for groups of elements, and it extends Iterable, which is what makes the enhanced for loop work.
  • List keeps order and allows duplicates (ArrayList).
  • Set rejects duplicates (HashSet, LinkedHashSet, TreeSet).
  • Queue and Deque hand elements out from the ends (ArrayDeque, PriorityQueue).
  • Map stores key-value pairs. It is part of the framework but does not extend Collection.

Declare variables, parameters and return types with the interface and choose the class only where you create the object: List<Order> orders = new ArrayList<>();. A method that accepts Collection<String> works with any list, set or deque, and you can swap HashMap for TreeMap later by changing one line.

Generics and boxing

Collections hold object references, never primitives. List<Integer> stores boxed Integer objects and the compiler boxes and unboxes for you. That costs memory for very large numeric data, and a null element unboxed into an int throws a NullPointerException. Never use raw types such as List without a type argument: the compiler can no longer catch a wrong element type.

Helper classes

Collections (plural) has static helpers such as max, frequency and unmodifiableList. Arrays.asList wraps an array as a fixed-size list: you can set elements but not add or remove them.

Mistakes that reach production

  • Removing elements inside a for-each loop. The iterator usually notices the change and throws ConcurrentModificationException, but the check is best-effort: removing the second-to-last element ends the loop early and silently skips the last one. Use removeIf or an explicit Iterator.remove().
  • Returning an internal mutable list from a service. Any caller can then change your state. Return List.copyOf(items) or an unmodifiable view.
  • Sharing a plain ArrayList or HashMap between request threads. These classes are not thread-safe; use the concurrent collections or immutable ones.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Declare repository methods as returning List<Order> so the implementation can change without touching callers.
  • Write one validation helper that accepts Collection<String> and reuse it for request lists and tag sets.
  • Replace a for-each loop that removes expired sessions with removeIf to stop ConcurrentModificationException in a cleanup job.

More examples

Removing while iterating

The for-each loop uses a hidden iterator, and removing through the list instead of the iterator makes its next step fail. removeIf does the same job in one pass and cannot get this wrong.

Example · java
void main() {
    List<String> sessions = new ArrayList<>(
            List.of("s1:expired", "s2:active", "s3:expired", "s4:active"));

    try {
        for (String session : sessions) {
            if (session.endsWith("expired")) {
                sessions.remove(session);     // changes the list under the iterator
            }
        }
    } catch (ConcurrentModificationException e) {
        IO.println("for-each remove failed: " + e.getClass().getSimpleName());
    }

    sessions.removeIf(session -> session.endsWith("expired"));
    IO.println("after removeIf: " + sessions);
}
// Output:
// for-each remove failed: ConcurrentModificationException
// after removeIf: [s2:active, s4:active]

Collections and Arrays helpers

Arrays.asList is a fixed-size view over the array, so set changes the array too and add throws. Copy it into new ArrayList<>(...) when you need a list that grows.

Example · java
void main() {
    List<Integer> latenciesMs = new ArrayList<>(List.of(120, 85, 240, 85, 99));
    IO.println("max: " + Collections.max(latenciesMs));
    IO.println("min: " + Collections.min(latenciesMs));
    IO.println("85ms seen: " + Collections.frequency(latenciesMs, 85));

    String[] array = {"a", "b", "c"};
    List<String> fixed = Arrays.asList(array);
    fixed.set(0, "z");                    // writes through to the array
    IO.println("list: " + fixed + ", array[0]: " + array[0]);
    try {
        fixed.add("d");
    } catch (UnsupportedOperationException e) {
        IO.println("Arrays.asList cannot grow");
    }
}
// Output:
// max: 240
// min: 85
// 85ms seen: 2
// list: [z, b, c], array[0]: z
// Arrays.asList cannot grow

Not leaking internal state from a service

Returning the field itself would let any controller add or clear orders behind the store's back. List.copyOf gives each caller a stable, read-only snapshot.

Example · java
record Order(String id, int totalCents) {}

class OrderStore {
    private final List<Order> orders = new ArrayList<>();

    void add(Order order) {
        orders.add(order);
    }

    List<Order> all() {
        return List.copyOf(orders);       // a snapshot callers cannot modify
    }
}

void main() {
    var store = new OrderStore();
    store.add(new Order("o-1", 2500));
    store.add(new Order("o-2", 990));

    List<Order> snapshot = store.all();
    store.add(new Order("o-3", 4200));

    IO.println("snapshot size: " + snapshot.size());
    IO.println("store size: " + store.all().size());
    try {
        snapshot.add(new Order("o-x", 1));
    } catch (UnsupportedOperationException e) {
        IO.println("callers cannot modify the store");
    }
}
// Output:
// snapshot size: 2
// store size: 3
// callers cannot modify the store

Discussion

  • Be the first to comment on this lesson.