Java · Lesson 5 of 6
Collections Framework
ArrayList, HashMap, and when to pick each.
- Intermediate
- 17 min read
- 3 objectives
Before this lessonLesson 4: Classes and Objects
What you will learn
- Use ArrayList add/get
- Store key-value in HashMap
- Iterate with for-each
Arrays have a fixed size. Real programs need data structures that grow, shrink and search efficiently, and Java's Collections Framework provides them in java.util. The three interfaces to know are List (ordered, duplicates allowed), Set (no duplicates) and Map (key-value pairs).
Arrays are fixed, collections grow
A plain array has a size that never changes. Real programs constantly add and remove items, look things up and remove duplicates. The Java Collections Framework provides ready-made containers for these jobs. The three you will use most: List (ordered, allows duplicates), Set (no duplicates) and Map (key to value, like a dictionary).
List and ArrayList
ArrayList is the default list. The angle brackets are generics: they tell the compiler what the list holds, so mistakes are caught at compile time instead of at runtime.
import java.util.*;
List<String> names = new ArrayList<>();
names.add("Ada");
names.add("Linus");
names.add("Grace");
System.out.println(names.get(1));
System.out.println(names.size());
names.remove("Linus");
System.out.println(names);Linus 3 [Ada, Grace]
Set and HashSet
A HashSet silently ignores duplicates and answers "is this in here?" in roughly constant time. Use LinkedHashSet to keep insertion order or TreeSet for sorted order.
Set<Integer> seen = new HashSet<>();
System.out.println(seen.add(5)); // true
System.out.println(seen.add(5)); // false, already present
System.out.println(seen.contains(5));true false true
Map and HashMap
A Map stores values by key. getOrDefault and merge make counting easy without null checks.
Map<String, Integer> counts = new HashMap<>();
for (String w : "to be or not to be".split(" ")) {
counts.merge(w, 1, Integer::sum);
}
System.out.println(counts.get("to"));
for (Map.Entry<String, Integer> e : counts.entrySet()) {
System.out.println(e.getKey() + "=" + e.getValue());
}2 (entries in hash order, for example: not=1, be=2, or=1, to=2)
Choosing the right one
- Need order and index access?
ArrayList. - Need fast add/remove at both ends?
ArrayDeque. - Need uniqueness or fast contains?
HashSet. - Need lookup by key?
HashMap. - Need keys kept sorted?
TreeMap/TreeSet.
Sorting and streams
List<Integer> nums = new ArrayList<>(List.of(5, 2, 9, 1));
Collections.sort(nums);
System.out.println(nums);
List<Integer> big = nums.stream().filter(n -> n > 2).toList();
System.out.println(big);[1, 2, 5, 9] [5, 9]
List: an ordered, growable sequence
import java.util.*;
public class Main {
public static void main(String[] args) {
List<String> tasks = new ArrayList<>();
tasks.add("write");
tasks.add("test");
tasks.add("ship");
tasks.remove("test");
System.out.println(tasks);
System.out.println(tasks.size() + " tasks, first = " + tasks.get(0));
System.out.println(tasks.contains("ship"));
}
}[write, ship] 2 tasks, first = write true
The <String> is a generic: it tells the compiler this list only holds strings, so adding a number is a compile error.
Set: uniqueness for free
import java.util.*;
public class Main {
public static void main(String[] args) {
Set<String> tags = new TreeSet<>();
tags.add("java");
tags.add("sql");
tags.add("java"); // duplicate ignored
System.out.println(tags);
System.out.println(tags.size());
}
}[java, sql] 2
Map: look things up by key
import java.util.*;
public class Main {
public static void main(String[] args) {
Map<String, Integer> stock = new TreeMap<>();
stock.put("apple", 5);
stock.put("pear", 2);
stock.merge("apple", 3, Integer::sum); // add to existing
for (Map.Entry<String, Integer> e : stock.entrySet()) {
System.out.println(e.getKey() + " -> " + e.getValue());
}
System.out.println(stock.getOrDefault("kiwi", 0));
}
}apple -> 8 pear -> 2 0
Worked example: count word frequency
import java.util.*;
public class Main {
public static void main(String[] args) {
String text = "the cat and the hat and the bat";
Map<String, Integer> counts = new TreeMap<>();
for (String w : text.split(" ")) {
counts.merge(w, 1, Integer::sum);
}
System.out.println(counts);
}
}{and=2, bat=1, cat=1, hat=1, the=3}Key takeaways
Listkeeps order and allows duplicates;Setrejects duplicates;Mapstores key-value pairs.- Generics like
List<String>make the compiler check element types. - Program to the interface (
List) and choose the implementation (ArrayList) on the right. Map.mergeis a neat way to count things.
// Write your solution here
