sublist
ArrayList
Java
programming
performance

Get a sublist from an ArrayList efficiently

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In Java, the fastest way to take a range from an ArrayList is usually not to copy anything at all. The List.subList method returns a view backed by the original list, which is efficient, but that efficiency comes with rules that matter in real code.

What subList Actually Returns

The signature is:

java
List<E> subList(int fromIndex, int toIndex)

The fromIndex is inclusive and toIndex is exclusive. More importantly, the returned value is a view, not a new independent ArrayList.

java
1import java.util.ArrayList;
2import java.util.List;
3
4public class SubListViewDemo {
5    public static void main(String[] args) {
6        ArrayList<String> names = new ArrayList<>();
7        names.add("Ada");
8        names.add("Grace");
9        names.add("Linus");
10        names.add("Margaret");
11
12        List<String> window = names.subList(1, 3);
13        System.out.println(window); // [Grace, Linus]
14
15        window.set(0, "Katherine");
16        System.out.println(names);  // [Ada, Katherine, Linus, Margaret]
17    }
18}

Creating the view is effectively O(1). Java does not duplicate the selected elements when you call subList, so it is the most efficient option when you only need a temporary slice for iteration, searching, or in-place updates.

When a View Is Exactly What You Want

A backed view is useful when you want changes in the range to affect the original list. Common examples include pagination logic, processing batches, or applying transformations to a section of the list.

java
1import java.util.ArrayList;
2import java.util.List;
3
4public class BatchUpdateDemo {
5    public static void main(String[] args) {
6        ArrayList<Integer> numbers = new ArrayList<>();
7        for (int i = 1; i <= 10; i++) {
8            numbers.add(i);
9        }
10
11        List<Integer> middle = numbers.subList(3, 7);
12        middle.replaceAll(value -> value * 10);
13
14        System.out.println(numbers);
15        // [1, 2, 3, 40, 50, 60, 70, 8, 9, 10]
16    }
17}

This is efficient because the range operation avoids allocating another list and avoids copying elements before the transformation runs.

When You Should Copy the Sublist

If you need the selected elements to live independently from the original list, wrap the view in a new ArrayList.

java
1import java.util.ArrayList;
2import java.util.Arrays;
3import java.util.List;
4
5public class IndependentCopyDemo {
6    public static void main(String[] args) {
7        List<String> source = new ArrayList<>(Arrays.asList(
8            "red", "green", "blue", "yellow"
9        ));
10
11        List<String> copy = new ArrayList<>(source.subList(1, 3));
12        source.clear();
13
14        System.out.println(copy); // [green, blue]
15    }
16}

This costs O(k) time and space for a range of size k, but it avoids coupling. That is often the safer design in APIs, multistep business logic, and tests where the source list may change later.

Performance and Safety Tradeoffs

For an ArrayList, random element access inside the sublist remains fast because the underlying structure is still array-based. The main danger is structural modification of the parent list while the view is in use.

For example, this pattern is fragile:

java
List<Integer> slice = numbers.subList(2, 5);
numbers.add(99);          // structural change on parent
System.out.println(slice); // may throw ConcurrentModificationException

The reason is that the sublist expects the original list structure to remain stable. A size-changing operation on the parent invalidates the view.

If you only read through the sublist immediately and discard it, subList is ideal. If the list may be resized later, copy the range instead.

Common Pitfalls

  • Treating subList as a copy. Changes to the view and the parent list are linked.
  • Forgetting that toIndex is exclusive. Off-by-one errors are common in range code.
  • Modifying the parent list structurally while a sublist is still in use. That can trigger ConcurrentModificationException.
  • Returning a live sublist from a public API without documenting it. Callers may mutate state you expected to remain internal.

Summary

  • 'subList is the most efficient way to get a range from an ArrayList because it creates a view.'
  • The returned list is backed by the original list, so updates are shared.
  • Wrap it in new ArrayList<>(...) when you need an independent copy.
  • 'fromIndex is inclusive and toIndex is exclusive.'
  • The main risk is structural modification of the parent list after the view is created.

Course illustration
Course illustration

All Rights Reserved.