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
| Interface | What it is | Typical implementation | Use it for |
|---|---|---|---|
List<E> | ordered, allows duplicates | ArrayList | order lines, results |
Set<E> | no duplicates | HashSet, TreeSet (sorted) | unique IDs, “have I seen this?” |
Map<K,V> | key → value | HashMap, 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);
HashMaporder is not guaranteed. UseLinkedHashMapfor insertion order orTreeMapfor sorted keys.- Objects as keys need
equalsandhashCode. Records provide them automatically. geton a missing key returnsnull. PrefergetOrDefaultorcontainsKey.
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.ofcreate immutable collections.- Use
removeIf,getOrDefaultandmergeto avoid the classic mistakes.