Skip to content

Collections and Generics

The Java Collections Framework separates interfaces such as List, Set, Queue, and Map from implementations with different performance and ordering properties.

Program to the required contract

static List<String> normalizedNames(Collection<String> names) {
    List<String> result = new ArrayList<>(names.size());
    for (String name : names) {
        result.add(name.strip().toLowerCase(Locale.ROOT));
    }
    return List.copyOf(result);
}

The parameter requires only iteration and size, while the return contract is an unmodifiable list. “Unmodifiable” does not make referenced mutable elements deeply immutable.

Generics

Generics provide compile-time type safety. Java generics are invariant: List<Integer> is not a subtype of List<Number>. Wildcards express variance at an API boundary.

static double sum(List<? extends Number> values) {
    double total = 0;
    for (Number value : values) total += value.doubleValue();
    return total;
}

static void addDefaults(List<? super Integer> destination) {
    destination.add(0);
    destination.add(1);
}

The mnemonic PECS means producer-extends, consumer-super. A value read from ? super Integer is only known to be an Object; a non-null arbitrary Number cannot be safely added to ? extends Number.

Selection guide

Need Typical implementation
Indexed sequence ArrayList
Unique values, no iteration order requirement HashSet
Insertion-ordered set/map LinkedHashSet / LinkedHashMap
Sorted keys TreeSet / TreeMap
Stack or queue ArrayDeque
Priority access PriorityQueue

Common mistakes

  • modifying a collection during enhanced iteration outside the iterator contract;
  • using mutable objects as hash keys;
  • assuming HashMap iteration order;
  • returning a mutable internal collection directly;
  • selecting a concurrent collection without understanding its atomic operations.