IntermediateJava · Lesson 3 of 9

The Collections Framework

List, Set, Map and Deque — choosing the right one, iterating, sorting with Comparator.

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.

CollectionsDemo.javaJava
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.of for fixed data.
  • Comparator.comparing(...).thenComparing(...) and Map.merge/computeIfAbsent cover 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.

WordStats.javaJava
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);
    }
}

Check your understanding

  1. Which collection keeps items unique and in sorted order?

  2. Why declare List<String> names = new ArrayList<>() rather than ArrayList<String> names?

  3. What happens when you call add on a list made with List.of(...)?

  4. What does counts.merge(word, 1, Integer::sum) do?

Ask AI