Java Collections

Choose the right collection: lists, sets, queues, stacks and sorted maps, plus Comparator sorting and the Collections helper class.

Lesson 50 of 127Intermediate17 min read

You've used two collections already: ArrayList for an ordered list and HashMap for key-value pairs. They're the ones you'll reach for most often, but they're not always the best fit. What if you need values kept in sorted order automatically? A waiting line where the first in is the first out? An "undo" history where the last action is undone first? A to-do list that always hands you the most urgent task?

Java's collections framework has a ready-made class for each of these, built on a few shared interfaces: List, Set, Queue and Map. Because they share interfaces, they also share many methods, so once you know one, the others feel familiar.

In this lesson you'll meet the main collection types, learn which to choose for a job, sort with Comparator, and use the Collections helper class.

The four families

InterfaceHoldsAllows duplicatesMain classes
ListItems in order, by indexYesArrayList, LinkedList
SetUnique itemsNoHashSet, LinkedHashSet, TreeSet
Queue / DequeItems waiting to be processedYesArrayDeque, PriorityQueue
MapKey-value pairsUnique keysHashMap, LinkedHashMap, TreeMap

Declare variables with the interface type and create them with a class: List<String> names = new ArrayList<>();. Your code then depends only on what a list can do, so you can switch to another class later by changing one word.

Three kinds of Set

All sets ignore duplicates, but they differ in the order you get back:

  • HashSet: no guaranteed order, the fastest.
  • LinkedHashSet: remembers the order items were added.
  • TreeSet: keeps items sorted, and adds methods like first(), last(), ceiling() and headSet().

Maps come in the same three flavours: HashMap, LinkedHashMap and the sorted TreeMap. A sorted map is handy for things like scores by date, where you want to read the keys in order.

Queues, stacks and priority

A queue is first in, first out, like people waiting at a till. A stack is last in, first out, like a pile of plates. ArrayDeque (a double-ended queue) can be both:

As a queueAs a stack
offer(x) adds to the backpush(x) adds to the top
poll() removes from the frontpop() removes from the top
peek() looks at the frontpeek() looks at the top

A PriorityQueue always hands out the smallest item first (or the most important, with a custom order), whatever order items were added in. It's the classic tool for task schedulers and "next most urgent job".

Sorting with Comparator and the Collections class

list.sort() takes a Comparator that says how to order two items. Build one from a method reference:

Java
people.sort(Comparator.comparing(Person::age));
people.sort(Comparator.comparing(Person::age).reversed());
people.sort(Comparator.comparing(Person::city).thenComparing(Person::name));

The Collections class adds helpers: Collections.max(list), Collections.min(), Collections.shuffle(), Collections.frequency(list, x) and Collections.unmodifiableList(). List.of(...), Set.of(...) and Map.of(...) create small immutable collections, which throw an exception if you try to change them.

Performance at a glance

The right collection can make a program dramatically faster. Typical costs, using big-O notation where O(1) means "about the same time however large the collection" and O(n) means "time grows with the number of items":

CollectionAddFind by indexcontainsRemove
ArrayListO(1) at the endO(1)O(n)O(n)
LinkedListO(1) at either endO(n)O(n)O(1) at either end
HashSet / HashMapO(1)n/aO(1)O(1)
TreeSet / TreeMapO(log n)n/aO(log n)O(log n)
ArrayDequeO(1) at either endn/aO(n)O(1) at either end
PriorityQueueO(log n)n/aO(n)O(log n) for the head

The pattern to remember: lists are for order and position, hash-based collections are for fast lookups, and tree-based ones are for sorted data. The complexity lesson explains big-O notation, and the collections framework lesson maps out the interfaces in more detail.

Java Collections Syntax

Collections at a glance

List<String> list = new ArrayList<>();
Set<String> set = new TreeSet<>();
Map<String, Integer> map = new TreeMap<>();
Deque<String> stack = new ArrayDeque<>();   // push, pop, peek
Queue<String> queue = new ArrayDeque<>();   // offer, poll, peek
Queue<Integer> pq = new PriorityQueue<>();
list.sort(Comparator.comparing(Item::price).reversed());
Collections.max(list);
List<String> fixed = List.of("a", "b");

Java Collections Summary

  • List, Set, Queue and Map are the main collection interfaces; declare variables with the interface.
  • HashSet is fastest, LinkedHashSet keeps insertion order and TreeSet keeps items sorted.
  • ArrayDeque works as a queue (offer, poll) or a stack (push, pop); PriorityQueue gives the smallest first.
  • Comparator.comparing, reversed() and thenComparing() describe sort orders.
  • The Collections class has max, min, shuffle and frequency; List.of creates an immutable list.

Java Collections Examples

Each example shows the code, the output it produces and what each part does.

Example 1

Three sets, three orders

Java
import java.util.*;

public class Main {
    public static void main(String[] args) {
        List<String> visits = List.of("Leeds", "Lyon", "Leeds", "Porto", "Lisbon", "Lyon");

        Set<String> hash = new HashSet<>(visits);
        Set<String> linked = new LinkedHashSet<>(visits);
        TreeSet<String> tree = new TreeSet<>(visits);

        System.out.println("Unique cities: " + hash.size());
        System.out.println("In visit order: " + linked);
        System.out.println("Sorted: " + tree);
        System.out.println("First alphabetically: " + tree.first());
        System.out.println("Seen Paris? " + hash.contains("Paris"));
    }
}
Output
Unique cities: 4
In visit order: [Leeds, Lyon, Porto, Lisbon]
Sorted: [Leeds, Lisbon, Lyon, Porto]
First alphabetically: Leeds
Seen Paris? false
Line-by-line explanation
  1. Line 5List.of creates a fixed list with two duplicates.
  2. Lines 7 to 9Each set drops the duplicates. Passing a collection to the constructor copies all its items.
  3. Line 12LinkedHashSet keeps the order of first appearance.
  4. Line 13TreeSet sorts the cities alphabetically.
Example 2

A queue, a stack and a priority queue

Java
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Queue<String> till = new ArrayDeque<>();
        till.offer("Ana");
        till.offer("Ben");
        till.offer("Cy");
        System.out.println("Serving " + till.poll() + ", next is " + till.peek());

        Deque<String> undo = new ArrayDeque<>();
        undo.push("type 'Hello'");
        undo.push("make bold");
        undo.push("delete line");
        System.out.println("Undo: " + undo.pop());

        Queue<Integer> urgent = new PriorityQueue<>(List.of(5, 1, 4, 2));
        System.out.print("By priority:");
        while (!urgent.isEmpty()) {
            System.out.print(" " + urgent.poll());
        }
        System.out.println();
    }
}
Output
Serving Ana, next is Ben
Undo: delete line
By priority: 1 2 4 5
Line-by-line explanation
  1. Lines 5 to 9A queue serves people in the order they arrived.
  2. Lines 11 to 15A stack undoes the most recent action first.
  3. Lines 17 to 21A priority queue always gives back the smallest number, whatever order they were added in.
Example 3

Sorting objects with Comparator

Java
import java.util.*;

public class Main {
    record Player(String name, String team, int score) {}

    public static void main(String[] args) {
        List<Player> players = new ArrayList<>(List.of(
            new Player("Ana", "Red", 120),
            new Player("Ben", "Blue", 340),
            new Player("Cleo", "Red", 340),
            new Player("Dev", "Blue", 90)
        ));

        players.sort(Comparator.comparing(Player::score).reversed()
                .thenComparing(Player::name));
        for (Player p : players) {
            System.out.printf("%-5s %-5s %d%n", p.name(), p.team(), p.score());
        }

        Player top = Collections.max(players, Comparator.comparing(Player::score));
        System.out.println("A top scorer: " + top.name());
    }
}
Output
Ben   Blue  340
Cleo  Red   340
Ana   Red   120
Dev   Blue  90
A top scorer: Ben
Line-by-line explanation
  1. Line 4A record is a compact data class, covered in the records lesson.
  2. Lines 14 to 15Highest score first; players with the same score are ordered by name.
  3. Line 20Collections.max finds the largest by the given order. With a tie, it returns the first one it meets in the list.

Common Java Collections Mistakes

Expecting a HashSet to keep order

Don't
Set<String> steps = new HashSet<>(List.of("wash", "cut", "cook"));
System.out.println(steps);     // order isn't guaranteed
Do
Set<String> steps = new LinkedHashSet<>(List.of("wash", "cut", "cook"));

Changing a List.of list

Don't
List<String> days = List.of("Mon", "Tue");
days.add("Wed");               // UnsupportedOperationException
Do
List<String> days = new ArrayList<>(List.of("Mon", "Tue"));
days.add("Wed");

Using the old Stack class

Don't
Stack<String> history = new Stack<>();
Do
Deque<String> history = new ArrayDeque<>();

Stack is an old, slower class; Java's documentation recommends ArrayDeque.

Java Collections in Real Projects

Collections in practice

Web servers queue incoming jobs in a Queue, editors keep undo history in a Deque, schedulers and route planners rely on PriorityQueue, and leaderboards keep scores in a TreeMap so the top entries are always ready. Choosing the right collection is one of the simplest ways to make Java code clearer and faster.

Java Collections FAQ

What is the difference between Collection and Collections?

Collection is the interface that List, Set and Queue extend; it declares shared methods such as add, remove, size and contains. Collections is a utility class full of static helper methods such as sort, max, shuffle and unmodifiableList.

Can Java collections hold primitive types?

No. Collections hold objects, so use wrapper types: List<Integer>, Set<Double>, Map<String, Boolean>. Java autoboxes values for you. For very large amounts of numeric data, a primitive array or an IntStream avoids the extra memory that wrapper objects use.

What is the difference between offer and add, or poll and remove, on a queue?

They do the same job but handle failure differently. add and remove throw an exception when they can't succeed, for example when a fixed-capacity queue is full or a queue is empty. offer returns false and poll returns null instead. With ArrayDeque and PriorityQueue, poll on an empty queue is the common case to handle.

How do I store my own objects in a TreeSet?

A TreeSet must be able to compare its items. Either make the class implement Comparable with a compareTo method, or pass a Comparator when you create the set: new TreeSet<>(Comparator.comparing(Person::name)). Without either, adding an item throws a ClassCastException.

Why shouldn't I use Vector, Stack or Hashtable?

They are older classes from Java 1.0 that synchronise every method, which makes them slower without making typical code correct across threads. Use ArrayList instead of Vector, ArrayDeque instead of Stack, and HashMap or ConcurrentHashMap instead of Hashtable.

Java Collections Exercises

Answer each one and check it instantly. If you get stuck, open the hint before the answer.

Fill in the blankseasy

Complete the line so the set keeps its items sorted.

Set<String> names = new <>();
Multiple choiceeasy

Which collection rejects duplicate items?

Predict the outputmedium

What does this print?

Deque<Integer> d = new ArrayDeque<>();
d.push(1); d.push(2); d.push(3);
System.out.println(d.pop());
Predict the outputmedium

What does this print?

Queue<Integer> pq = new PriorityQueue<>(List.of(8, 3, 6));
System.out.println(pq.poll());
Multiple choicehard

Which Comparator sorts players by score, highest first?

Java Collections Quiz

5 questions to check what you learned.

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

    • Code depends only on the List interface, so the class can be swapped easily
    • It's faster
    • ArrayList can't be a variable type
    • It saves memory
  2. Which collection keeps items in the order they were added, without duplicates?

    • HashSet
    • LinkedHashSet
    • TreeSet
    • PriorityQueue
  3. Which method removes and returns the front of a Queue?

    • peek()
    • push()
    • poll()
    • get()
  4. What does Collections.frequency(list, "a") return?

    • Whether "a" is in the list
    • The index of "a"
    • A sorted list
    • How many times "a" appears in the list
  5. Which class does Java's documentation recommend instead of Stack?

    • ArrayDeque
    • LinkedList
    • Vector
    • PriorityQueue