Nested collections

Collections · lesson 26 of 42 · 4 min read

Build a map of lists, and know when a copy still shares the collections inside it.

Open this lesson in the learning hub

Key points

  • A map of lists is the everyday grouping structure: one key, many values, no separate class needed.
  • Fill it with map.computeIfAbsent(key, k -> new ArrayList<>()).add(value) — no null check anywhere.
  • Read it with getOrDefault(key, List.of()) so a missing key gives an empty list, not null.
  • Counters nest the same way: computeIfAbsent for the inner map, then merge on it.
  • new HashMap<>(other) is a shallow copy. The inner lists are shared, so edits show up in both maps.
  • For a real copy, rebuild each inner collection. Or store immutable inner lists so nobody can edit them at all.

Example

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Map<String, List<String>> byTeam = new LinkedHashMap<>();
        String[][] rows = {{"eng", "ana"}, {"ops", "bo"}, {"eng", "cy"}, {"ops", "di"}};
        for (String[] r : rows) {
            byTeam.computeIfAbsent(r[0], k -> new ArrayList<>()).add(r[1]);
        }
        System.out.println("grouped           : " + byTeam);
        System.out.println("eng members       : " + byTeam.getOrDefault("eng", List.of()));
        System.out.println("qa members        : " + byTeam.getOrDefault("qa", List.of()) + "   (no null check needed)");

        Map<String, Map<String, Integer>> nested = new LinkedHashMap<>();
        nested.computeIfAbsent("eu", k -> new LinkedHashMap<>()).merge("orders", 2, Integer::sum);
        nested.computeIfAbsent("eu", k -> new LinkedHashMap<>()).merge("orders", 3, Integer::sum);
        nested.computeIfAbsent("us", k -> new LinkedHashMap<>()).merge("orders", 1, Integer::sum);
        System.out.println("nested counters   : " + nested);

        Map<String, List<String>> shallow = new LinkedHashMap<>(byTeam);
        shallow.get("eng").add("eve");
        System.out.println("shallow copy add  : " + byTeam.get("eng") + "   (original changed too)");

        Map<String, List<String>> deep = new LinkedHashMap<>();
        byTeam.forEach((k, v) -> deep.put(k, new ArrayList<>(v)));
        deep.get("eng").add("fay");
        System.out.println("deep copy add     : " + deep.get("eng"));
        System.out.println("original after it : " + byTeam.get("eng") + "   (untouched)");
    }
}

computeIfAbsent builds the inner collection; only a rebuilt copy stops two maps sharing it.

This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Collections course, and every lesson in it is listed on the Collections contents page.