What you'll learn

  • When to use List, Set or Map
  • ArrayList, HashSet, TreeSet, HashMap and TreeMap
  • Immutable collections with List.of and Map.of
  • Safe removal, merging and sorting

Most real data comes in groups: the lines on an order, the set of SKUs in stock, a lookup from customer ID to customer. Java’s Collections Framework gives you ready-made, well-tested containers for all of them.

The three you’ll use every day

InterfaceWhat it isTypical implementationUse it for
List<E>ordered, allows duplicatesArrayListorder lines, results
Set<E>no duplicatesHashSet, TreeSet (sorted)unique IDs, “have I seen this?”
Map<K,V>key → valueHashMap, TreeMap (sorted keys), LinkedHashMap (insertion order)lookups, counts, grouping

Declare variables with the interface and create with an implementation: List<String> skus = new ArrayList<>();. That way you can switch implementations later without touching the rest of the code. The <String> part is generics — the next lesson.

List

List<String> skus = new ArrayList<>();
skus.add("AX-200");
skus.add("BX-310");
skus.get(0);            // "AX-200"
skus.size();            // 2
skus.contains("BX-310");// true
skus.remove("AX-200");

Set

Set<String> unique = new HashSet<>(List.of("AX-200", "BX-310", "AX-200"));
unique.size();          // 2 — the duplicate is ignored
Set<String> sorted = new TreeSet<>(List.of("pear", "apple", "fig"));   // [apple, fig, pear]

Map

Map<String, Integer> stock = new HashMap<>();
stock.put("AX-200", 12);
stock.get("AX-200");                    // 12
stock.getOrDefault("CX-420", 0);        // 0 instead of null
stock.merge("AX-200", 5, Integer::sum); // 17 — add to the existing value
for (Map.Entry<String, Integer> e : stock.entrySet()) {
    System.out.println(e.getKey() + " = " + e.getValue());
}

merge is perfect for counting and totalling: “add this amount to the customer’s total, or start at this amount”.

Immutable collections

List<Integer> fixed = List.of(1, 2, 3);
Map<String, String> codes = Map.of("ST", "ship-to", "BT", "bill-to");

These can’t be changed — trying throws UnsupportedOperationException. Use them for constants and for returning data you don’t want callers to modify. (They also don’t allow null elements.)

Classic mistakes

Watch out: Removing items inside a for-each loop throws ConcurrentModificationException. Use removeIf instead: numbers.removeIf(n -> n % 2 == 0);

  • HashMap order is not guaranteed. Use LinkedHashMap for insertion order or TreeMap for sorted keys.
  • Objects as keys need equals and hashCode. Records provide them automatically.
  • get on a missing key returns null. Prefer getOrDefault or containsKey.

Sorting

names.sort(String.CASE_INSENSITIVE_ORDER);
orders.sort(Comparator.comparingDouble(Order::amount).reversed());

The second line uses a method reference — you’ll learn those in Method references.

The full example

// CollectionsDemo.java — List, Set, Map and iterating safely.
import java.util.*;

public class CollectionsDemo {

    public static void main(String[] args) {
        // List: ordered, allows duplicates
        List<String> skus = new ArrayList<>();
        skus.add("AX-200");
        skus.add("BX-310");
        skus.add("AX-200");
        System.out.println(skus + " size=" + skus.size());   // [AX-200, BX-310, AX-200] size=3
        System.out.println("First: " + skus.get(0));

        // Set: no duplicates
        Set<String> unique = new HashSet<>(skus);
        System.out.println("Unique count: " + unique.size());  // 2

        // TreeSet keeps items sorted
        Set<String> sorted = new TreeSet<>(List.of("pear", "apple", "fig"));
        System.out.println(sorted);                            // [apple, fig, pear]

        // Map: key -> value
        Map<String, Integer> stock = new HashMap<>();
        stock.put("AX-200", 12);
        stock.put("BX-310", 0);
        stock.merge("AX-200", 5, Integer::sum);                // add 5 to existing value
        System.out.println("AX-200 stock: " + stock.get("AX-200"));               // 17
        System.out.println("CX-420 stock: " + stock.getOrDefault("CX-420", 0));    // 0

        for (Map.Entry<String, Integer> e : new TreeMap<>(stock).entrySet()) {
            System.out.println(e.getKey() + " = " + e.getValue());
        }

        // Immutable collections (Java 9+)
        List<Integer> fixed = List.of(1, 2, 3);
        try {
            fixed.add(4);
        } catch (UnsupportedOperationException e) {
            System.out.println("List.of(...) cannot be changed");
        }

        // Remove while iterating: use removeIf (not a for-each loop)
        List<Integer> numbers = new ArrayList<>(List.of(1, 2, 3, 4, 5, 6));
        numbers.removeIf(n -> n % 2 == 0);
        System.out.println(numbers);                           // [1, 3, 5]

        // Sorting
        List<String> names = new ArrayList<>(List.of("Globex", "acme", "Initech"));
        names.sort(String.CASE_INSENSITIVE_ORDER);
        System.out.println(names);                             // [acme, Globex, Initech]
    }
}

Try it yourself

Given a list of words, count how many times each word appears and print the counts in alphabetical order.

Show a solution
List<String> words = List.of("ok", "error", "ok", "warn", "error", "ok");
Map<String, Integer> counts = new TreeMap<>();
for (String w : words) {
    counts.merge(w, 1, Integer::sum);
}
System.out.println(counts);   // {error=2, ok=3, warn=1}

A TreeMap keeps the keys sorted. In the Advanced streams lesson you’ll do this in one line with groupingBy.

Key takeaways

  • List for ordered items, Set for uniqueness, Map for lookups.
  • Declare with the interface, create with an implementation.
  • List.of / Map.of create immutable collections.
  • Use removeIf, getOrDefault and merge to avoid the classic mistakes.

Example files