Lists and the Collections Framework

Java · Practical · 12 min

Lists are the most used data structure in Java code; using them idiomatically is a daily skill.

The Collections Framework in java.util provides interfaces (List, Set, Map, Queue, Deque) and implementations. You program to the interface and pick the implementation once, at construction.

A List is an ordered sequence that allows duplicates and index access. ArrayList is the default choice: backed by a resizable array, fast get(i) and fast appends. LinkedList is rarely the right answer — despite theory, it is slower than ArrayList for almost every real workload. For queues and stacks, use ArrayDeque.

Core operations: add(x), add(i, x), get(i), set(i, x), remove(i) or remove(obj), size(), isEmpty(), contains(x), indexOf(x).

Create small fixed lists with List.of("a", "b"). These are unmodifiable: add throws UnsupportedOperationException, and they reject null elements. When you need a mutable copy, write new ArrayList<>(List.of(...)).

Sorting: list.sort(Comparator.naturalOrder()) or with a comparator built from key extractors:

people.sort(Comparator.comparing(Person::lastName).thenComparing(Person::age));

To remove elements matching a condition, use list.removeIf(x -> x.isBlank()) rather than removing inside a for-each loop, which throws ConcurrentModificationException.

Watch remove on a List<Integer>: list.remove(1) removes the element at index 1, while list.remove(Integer.valueOf(1)) removes the value 1.

Since Java 21, List is a SequencedCollection, giving getFirst(), getLast(), removeLast() and reversed().

Example

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

public class ListsDemo {
    record Person(String name, int age) { }

    public static void main(String[] args) {
        List<Person> people = new ArrayList<>(List.of(
                new Person("Cy", 41), new Person("Ana", 29), new Person("Bo", 29)));

        people.sort(Comparator.comparing(Person::age).thenComparing(Person::name));
        System.out.println(people.getFirst().name());    // Ana  (Java 21)
        people.removeIf(p -> p.age() > 40);
        System.out.println(people.size());               // 2

        List<Integer> nums = new ArrayList<>(List.of(5, 1, 7));
        nums.remove(1);                                  // removes index 1 -> [5, 7]
        nums.remove(Integer.valueOf(7));                 // removes value 7 -> [5]
        System.out.println(nums);                        // [5]

        List<String> fixed = List.of("x", "y");          // fixed.add("z") would throw
        System.out.println(fixed.reversed());            // [y, x]
    }
}

Remember

  • Declare List<T>, construct ArrayList — it's the right default
  • List.of is unmodifiable and null-hostile; copy into ArrayList to mutate
  • Sort with Comparator.comparing(...).thenComparing(...); filter out with removeIf

Watch out for

  • Calling remove(int) on a List<Integer> when you meant to remove a value.

Check yourself

  1. What happens on `List.of(1, 2).add(3)`?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.oracle.com/javase/tutorial/collections/interfaces/list.html