List Interface

Su Suriya Ravichandran Updated 14 Nov 2026
7 min read ·Lesson 81 of 133

java.util.List is the interface for ordered collections that allow duplicates and give access by position (index). It defines what every list can do, regardless of how it stores its elements. ArrayList, LinkedList, the immutable lists returned by List.of, and many others all implement it. Writing code against List rather than a specific class is one of the most important habits in Java programming.

Where List Fits in the Collections Framework

Iterable
  └── Collection
        ├── SequencedCollection        (Java 21)
        │     ├── List                 ordered, indexed, duplicates allowed
        │     │     ├── ArrayList
        │     │     ├── LinkedList
        │     │     └── CopyOnWriteArrayList, Vector, ...
        │     └── Deque
        ├── Set                        no duplicates
        └── Queue
Map                                     (separate hierarchy: key-value pairs)

A List is a Collection, so it supports everything collections do (add, remove, contains, size, iteration, streams) and adds operations based on position.

What Makes a List a List

PropertyMeaning
OrderedElements keep a defined sequence, normally the order in which they were added
IndexedEach element has a zero-based position; get(0) is the first
Duplicates allowedThe same value may appear many times
Positional equalityTwo lists are equal if they contain equal elements in the same order

Programming to the Interface

List<String> names = new ArrayList<>();      // declare as List, choose an implementation

public double average(List<Integer> scores) { ... }   // accept any List
public List<String> activeUsers() { ... }             // return a List

Declaring variables, parameters, and return types as List means:

  • Callers can pass any list implementation (ArrayList, LinkedList, List.of(...)).
  • The implementation can be changed in one place without touching other code.
  • The code expresses what it needs (an ordered, indexed sequence), not how it is stored.

Use a more specific type only when you need methods that List does not have, such as ArrayList.ensureCapacity or LinkedList's deque methods.

Positional Methods

These are the methods List adds on top of Collection:

MethodPurpose
get(int index)Element at a position
set(int index, E e)Replace an element; returns the old one
add(int index, E e)Insert, shifting later elements right
remove(int index)Remove by position; returns the removed element
indexOf(Object o) / lastIndexOf(Object o)First / last position of an element, or -1
subList(int from, int to)A view of a range of the list
listIterator()A two-way iterator that can also set and add
sort(Comparator)Sort in place (null for natural order)
replaceAll(UnaryOperator)Transform every element in place

Since Java 21, List also inherits from SequencedCollection:

MethodPurpose
getFirst(), getLast()First / last element (throws if empty)
addFirst(e), addLast(e)Insert at either end
removeFirst(), removeLast()Remove from either end
reversed()A reverse-ordered view of the list
List<String> steps = new ArrayList<>(List.of("plan", "build", "test"));
steps.addFirst("research");
System.out.println(steps.getLast());     // test
System.out.println(steps.reversed());    // [test, build, plan, research]

On older Java versions, use list.get(0) and list.get(list.size() - 1) instead.

Equality and hashCode

The List contract defines equals precisely: two lists are equal if they have the same size and equal elements at every position. The implementation class does not matter:

List<Integer> a = List.of(1, 2, 3);
List<Integer> b = new LinkedList<>(List.of(1, 2, 3));
System.out.println(a.equals(b));   // true
System.out.println(a.equals(List.of(3, 2, 1)));   // false: order matters

hashCode is also defined from the elements, so equal lists have equal hash codes and can be used as map keys (as long as they are not modified while in the map).

subList: A View, Not a Copy

subList(from, to) returns a live view of part of the list. The start is inclusive and the end exclusive. Changes through the view affect the original, which makes it a concise way to operate on a range:

List<Integer> nums = new ArrayList<>(List.of(1, 2, 3, 4, 5, 6));

nums.subList(1, 4).clear();               // remove indexes 1, 2, 3
System.out.println(nums);                 // [1, 5, 6]

Collections.sort(nums.subList(0, 2));     // sort only part of a list

Two rules apply:

  • A sublist is valid only while the backing list is not structurally modified (elements added or removed) by other means. Doing so and then using the sublist throws ConcurrentModificationException.
  • To keep a slice independently, copy it: new ArrayList<>(nums.subList(0, 3)) or List.copyOf(...).

Mutable, Unmodifiable, and Fixed-Size Lists

Not every List supports every operation. Methods like add and set are optional: an implementation may throw UnsupportedOperationException.

How createdgetsetadd / removenull elements
new ArrayList<>() / new LinkedList<>()✓✓✓✓
List.of(...), List.copyOf(...), stream.toList()✓✗✗✗ (toList() allows them)
Arrays.asList(array)✓✓ (writes to the array)✗✓
Collections.unmodifiableList(list)✓✗✗As backing list
List<String> fixed = List.of("a", "b");
fixed.set(0, "z");         // UnsupportedOperationException

The difference between an unmodifiable view and an immutable copy matters:

List<String> source = new ArrayList<>(List.of("a"));
List<String> view = Collections.unmodifiableList(source);
List<String> copy = List.copyOf(source);

source.add("b");
System.out.println(view);   // [a, b]  the view reflects changes to the source
System.out.println(copy);   // [a]     the copy does not

Return List.copyOf(...) from a method when callers should receive a snapshot; return an unmodifiable view when they should see live data but not change it.

Choosing a List Implementation

ImplementationStrengthsUse when
ArrayListFast indexed access, compact memoryDefault choice for almost all lists
LinkedListFast insert/remove at the ends and at an iterator positionMany insertions while iterating; need List and Deque together
List.of / List.copyOfImmutable, compact, thread-safe to shareConstants, return values, data that should not change
CopyOnWriteArrayListThread-safe; iteration never throwsRead-mostly lists shared between threads (such as listener lists)
VectorLegacy synchronized listAvoid in new code

Common List Operations

List<Integer> scores = new ArrayList<>(List.of(72, 88, 95, 61, 88));

int max = Collections.max(scores);                  // 95
int freq = Collections.frequency(scores, 88);       // 2
scores.sort(Comparator.reverseOrder());             // [95, 88, 88, 72, 61]
List<Integer> top3 = List.copyOf(scores.subList(0, 3));   // [95, 88, 88]
boolean anyFail = scores.stream().anyMatch(s -> s < 40);  // false

The Collections utility class, covered later in this section, adds many algorithms that work on any List: sort, reverse, shuffle, swap, binarySearch, frequency, nCopies, and more.

Practical Example: Playlist Tools

import java.util.*;

public class Playlist2 {
    static List<String> shuffleKeepingFirst(List<String> songs, long seed) {
        List<String> result = new ArrayList<>(songs);
        Collections.shuffle(result.subList(1, result.size()), new Random(seed));
        return result;
    }

    static <T> List<List<T>> chunk(List<T> items, int size) {
        List<List<T>> pages = new ArrayList<>();
        for (int i = 0; i < items.size(); i += size) {
            pages.add(List.copyOf(items.subList(i, Math.min(i + size, items.size()))));
        }
        return pages;
    }

    public static void main(String[] args) {
        List<String> queue = new ArrayList<>(List.of("Intro", "Song A", "Song B", "Song C", "Song D", "Outro"));

        queue.add(3, "Ad Break");
        queue.removeIf(s -> s.startsWith("Ad"));
        queue.replaceAll(String::toUpperCase);
        System.out.println("Queue:    " + queue);
        System.out.println("First/last: " + queue.getFirst() + " / " + queue.getLast());
        System.out.println("Reversed: " + queue.reversed());

        List<String> middle = queue.subList(1, queue.size() - 1);
        System.out.println("Middle:   " + middle);
        middle.clear();
        System.out.println("After clearing middle view: " + queue);

        List<String> tracks = List.of("t1", "t2", "t3", "t4", "t5", "t6", "t7");
        System.out.println("Pages:    " + chunk(tracks, 3));
        System.out.println("Shuffled: " + shuffleKeepingFirst(tracks, 42));
    }
}

Output (Java 21 or later, since it uses getFirst, getLast, and reversed):

Queue:    [INTRO, SONG A, SONG B, SONG C, SONG D, OUTRO]
First/last: INTRO / OUTRO
Reversed: [OUTRO, SONG D, SONG C, SONG B, SONG A, INTRO]
Middle:   [SONG A, SONG B, SONG C, SONG D]
After clearing middle view: [INTRO, OUTRO]
Pages:    [[t1, t2, t3], [t4, t5, t6], [t7]]
Shuffled: [t1, t3, t2, t6, t7, t5, t4]

Each step uses a List-level operation: positional add, conditional removeIf, in-place replaceAll, and the sequenced methods getFirst, getLast, and reversed. subList returns a view, so clearing middle removes those songs from queue itself. chunk uses subList in the opposite way, copying each slice with List.copyOf so that the pages are independent of the source; because it is written against List<T>, it works for any list of any element type. shuffleKeepingFirst shuffles only the view that excludes the first track, keeping the intro in place. Passing a seeded Random makes the shuffle repeatable, which is useful in tests; the exact shuffled order shown depends on the seed.

0 Comments

Reviewed before they appear

No comments yet.

Java
Java Introduction Java Features Java Syntax Java Comments Java Statements Java Output Java Variables Java Data Types Java Type Casting Java Operators Java Strings Java Numbers Java Math Java Booleans Java User Input Java Conditions Java If Else Java Switch Java While Loop Java For Loop Java Break Java Continue Java Arrays Java Methods Java Parameters Java Return Values Java Scope Java Recursion Install Java Install JDK Java IDEs Java JDK Java JRE Java JVM Java Compiler Java File Compile Java Run Java JShell Basics Java Packages Java OOP Java Classes Java Objects Class Methods Constructors Constructor Overload this Keyword static Keyword final Keyword Encapsulation Inheritance Polymorphism Abstraction Access Modifiers super Keyword Method Overload Method Override Instanceof Operator Nested Classes Inner Classes Anonymous Classes Enums Records Array Basics Array Length Loop Arrays Multidimensional Arrays Array Copy Array Sorting Array Search ArrayList LinkedList HashSet TreeSet HashMap TreeMap Queue Interface Deque Interface Stack Class Iterator List Interface Set Interface Map Interface Collections Class Comparable Comparator Generics Basics Generic Classes Generic Methods Wildcards String Basics String Length String Methods String Concatenation String Compare String Search String Replace String Split String Format StringBuilder StringBuffer Text Blocks Regular Expressions Number Types Integer Class Float Class Double Class BigInteger BigDecimal Math Class Random Numbers Number Format Date Basics LocalDate LocalTime LocalDateTime ZonedDateTime Date Formatting Date Parsing Period Class Duration Class Java Instant Class Java Exception Basics Java try...catch Java Multiple Catch Blocks Java finally Block Java throw Keyword Java throws Keyword Java Checked Exceptions Java Unchecked Exceptions Java Custom Exceptions Java try-with-resources Java Exception Chaining
Ask about this post
AI Ask about this post

Ask questions about List Interface and get answers drawn from it.

Signed-in readers only.