The Collections Framework
List, Set and Map — how they differ, how they perform, and how to choose.
What you’ll learn
- List vs Set vs Map
- ArrayList, HashSet and HashMap in practice
- Big-O cost of common operations
- Immutable collections with List.of and Map.of
The three shapes of data
Almost every collection you use is one of three interfaces:
| Interface | Think of it as | Duplicates? | Ordered? | Go-to implementation |
|---|---|---|---|---|
List | A numbered sequence | Yes | Yes (by index) | ArrayList |
Set | A bag of unique items | No | No* | HashSet |
Map | A dictionary of key → value | Unique keys | No* | HashMap |
* LinkedHashSet/LinkedHashMap keep insertion order; TreeSet/TreeMap keep sorted order.
List
List<String> skills = new ArrayList<>();
skills.add("Java");
skills.add("SQL");
skills.add("Java"); // duplicates allowed
skills.get(0); // "Java"
skills.size(); // 3
skills.contains("SQL"); // trueDeclare the variable as the interface (List) and pick the implementation on the right. That way you can change it later without touching other code.
Set
Set<String> tags = new HashSet<>();
tags.add("backend");
tags.add("backend"); // ignored
tags.size(); // 1Use a Set whenever the question is "have I seen this before?" — contains on a HashSet is O(1), compared with O(n) on a list.
Map
Map<String, Integer> stock = new HashMap<>();
stock.put("keyboard", 12);
stock.put("mouse", 40);
stock.get("mouse"); // 40
stock.getOrDefault("monitor", 0); // 0
stock.merge("mouse", 5, Integer::sum); // 45
for (var entry : stock.entrySet()) {
System.out.println(entry.getKey() + " -> " + entry.getValue());
}Performance at a glance
| Operation | ArrayList | HashSet | HashMap |
|---|---|---|---|
| Add | O(1) amortised | O(1) | O(1) |
| Lookup by index | O(1) | — | — |
contains / get by key | O(n) | O(1) | O(1) |
| Remove from middle | O(n) | O(1) | O(1) |
Common mistake
Objects used as HashMap keys or HashSet elements must implement equals() and hashCode() consistently. Records do this for you.
Immutable collections
List<String> roles = List.of("ADMIN", "USER");
Map<String, Integer> limits = Map.of("free", 10, "pro", 1000);
roles.add("GUEST"); // throws UnsupportedOperationExceptionReturn immutable collections from methods when callers shouldn't modify your data.
⚡ Try it yourself
Given a List<String> of words, build a Map<String, Integer> counting how many times each word appears. Then print only words that appear more than once.
Interview questions
QHow does HashMap work internally?+
It stores entries in an array of buckets. The key's hashCode() picks a bucket; collisions within a bucket are stored in a linked list, which Java 8+ converts to a balanced tree once it grows past 8 entries. equals() finds the exact key inside the bucket. When the map exceeds its load factor (0.75 by default) it doubles in size and rehashes.
QArrayList vs LinkedList — which would you pick?+
Almost always ArrayList. It has O(1) random access and far better cache locality. LinkedList only wins for frequent inserts/removes at the ends, and ArrayDeque usually beats it there too.
Key takeaways
- List = ordered with duplicates, Set = unique, Map = key → value.
- Default to
ArrayList,HashSetandHashMap. - Keys need correct
equals()andhashCode().
Finished this lesson?
Mark it complete to move forward on your roadmap.