Collections grow and shrink as needed, unlike arrays. List keeps order and allows duplicates (ArrayList); Set keeps unique items (HashSet, or TreeSet for sorted order); Map stores key → value pairs (HashMap, TreeMap for sorted keys, LinkedHashMap for insertion order); Deque works as a stack or queue (ArrayDeque).
Declare variables by interface (List<String> names = new ArrayList<>()) so you can change the implementation later. List.of(...) and Map.of(...) create unmodifiable collections.
Sort with Comparator: Comparator.comparing(Student::average).reversed().thenComparing(Student::name). Map methods such as getOrDefault, merge and computeIfAbsent make counting and grouping easy.
import java.util.*;
public class CollectionsDemo {
record Student(String name, int form, double average) {}
public static void main(String[] args) {
List<Student> students = new ArrayList<>(List.of(
new Student("Neema", 4, 75.0),
new Student("Amina", 4, 86.0),
new Student("Baraka", 3, 54.0),
new Student("Rehema", 3, 71.3),
new Student("Juma", 4, 54.0)
));
students.sort(Comparator.comparingDouble(Student::average).reversed()
.thenComparing(Student::name));
students.forEach(s -> System.out.println(s.name() + " " + s.average()));
Map<Integer, List<String>> byForm = new TreeMap<>();
for (Student s : students) {
byForm.computeIfAbsent(s.form(), f -> new ArrayList<>()).add(s.name());
}
System.out.println(byForm); // {3=[Rehema, Baraka], 4=[Amina, Neema, Juma]}
Map<String, Integer> clubCounts = new HashMap<>();
for (String club : List.of("debate", "science", "debate", "football", "science", "debate")) {
clubCounts.merge(club, 1, Integer::sum);
}
System.out.println(new TreeMap<>(clubCounts)); // {debate=3, football=1, science=2}
Set<String> maths = new TreeSet<>(List.of("Amina", "Juma", "Neema"));
Set<String> biology = Set.of("Amina", "Rehema");
Set<String> both = new TreeSet<>(maths);
both.retainAll(biology);
System.out.println("Both: " + both + ", Maths has Juma? " + maths.contains("Juma"));
Deque<String> undo = new ArrayDeque<>(); // used as a stack
undo.push("typed name");
undo.push("selected form");
System.out.println("Undo: " + undo.pop() + ", then: " + undo.peek());
List<String> fixed = List.of("A", "B");
try {
fixed.add("C");
} catch (UnsupportedOperationException e) {
System.out.println("List.of is unmodifiable");
}
}
}Key points
- List = ordered, Set = unique, Map = key → value, Deque = stack/queue.
- Declare by interface type; use
List.of/Map.offor fixed data. Comparator.comparing(...).thenComparing(...)andMap.merge/computeIfAbsentcover most sorting and grouping.
Exercise
Read a list of words (from an array) and print: the number of unique words, the 5 most frequent words with counts (using a Map and sorting its entries), and the words grouped by their first letter in a TreeMap.
Show solution
Try the exercise yourself first — then compare your approach with this one.
A HashMap counts each word with merge. Sorting the map's entries by count (then alphabetically) gives the top five. computeIfAbsent on a TreeMap groups the unique words by their first letter, with the letters in order.
import java.util.*;
public class WordStats {
public static void main(String[] args) {
String text = "the cell is the basic unit of life the cell membrane controls what enters the cell "
+ "and the nucleus controls the cell";
String[] words = text.split("\\s+");
Map<String, Integer> counts = new HashMap<>();
for (String w : words) counts.merge(w, 1, Integer::sum);
System.out.println("Unique words: " + counts.size());
List<Map.Entry<String, Integer>> top = new ArrayList<>(counts.entrySet());
top.sort(Map.Entry.<String, Integer>comparingByValue().reversed()
.thenComparing(Map.Entry.comparingByKey()));
System.out.println("Top 5: " + top.subList(0, 5));
Map<Character, SortedSet<String>> byLetter = new TreeMap<>();
for (String w : counts.keySet()) {
byLetter.computeIfAbsent(w.charAt(0), k -> new TreeSet<>()).add(w);
}
System.out.println(byLetter);
}
}