skip to content

What does List.subList(from, to) return, and how does it relate to the original list?

level: juniorimportance: must knowfreq 55%

answer

  1. View, not a copy
  2. Half-open range [from, to)
  3. Writes flow both ways
  4. O(1) to create
  5. subList(...).clear() deletes a range

basics

~20 s

subList(from, to) returns a view of part of the original list, not a copy. Changes to the view show up in the original list and vice versa. The range is from inclusive to to exclusive.

solid answer

~40 s

subList(from, to) returns a List that is a live view backed by the original list over the half-open range [from, to). It does not copy elements: setting, getting, or non-structural changes through the view read and write the same backing storage, so they are visible in both directions. The view's indices are offset, so view index 0 maps to original index from. Because it shares storage, subList is cheap to create regardless of range size. If you need an independent list, copy it explicitly, for example new ArrayList<>(list.subList(from, to)). A common, intended use is range operations: list.subList(from, to).clear() removes that range from the original list in one call.

code

java · 8 lines
java
List<Integer> nums = new ArrayList<>(List.of(10, 20, 30, 40, 50));
List<Integer> mid = nums.subList(1, 4); // view over [20, 30, 40]

mid.set(0, 99);            // writes through to the parent
System.out.println(nums);  // [10, 99, 30, 40, 50]

nums.subList(1, 4).clear(); // idiomatic range delete
System.out.println(nums);  // [10, 50]

go deeper

for a junior

Knows subList returns a view tied to the original, that the range is from inclusive to exclusive, and that to get a copy you wrap it in new ArrayList<>(...).

for a middle

Can explain the index offset and two-way visibility, and uses subList(...).clear() to delete a range. Knows creation is O(1).

for a senior

Articulates the backing-storage mechanism, distinguishes structural vs non-structural changes, and explains why the view is cheap regardless of size.

for a principal

Frames subList as a zero-copy view abstraction, discusses lifetime/scope discipline, fail-fast invalidation, and API design tradeoffs of returning views vs copies.

## What a List is A `List` in Java is an ordered collection where each element has an integer position (index) starting at 0. `ArrayList`, `LinkedList`, and `Arrays.asList(...)` are common implementations. ## What subList does `List.subList(int fromIndex, int toIndex)` returns another `List`. The range is **half-open**: `fromIndex` is **inclusive** and `toIndex` is **exclusive**. So `subList(1, 4)` covers original positions 1, 2, 3 — that is `toIndex - fromIndex` = 3 elements. `subList(2, 2)` is legal and returns an empty list. ## The key idea: it is a VIEW, not a copy A **view** means the returned list does not have its own separate array of elements. It is a thin wrapper object that holds a reference to the parent list plus the offset (`fromIndex`) and size. Every operation on the view is translated into an operation on the parent at `parentIndex = fromIndex + viewIndex`. Consequences: - **Two-way visibility.** `sub.set(0, x)` changes the parent at position `fromIndex`. Reading the parent afterwards shows `x`. Likewise, changing the parent's element at that position is seen through the view. - **No copy cost.** Creating the view is O(1) — it does not matter whether the range is 3 elements or 3 million. - **Index offset.** View index 0 = parent index `fromIndex`. ## Worked example ``` List<Integer> nums = new ArrayList<>(List.of(10, 20, 30, 40, 50)); List<Integer> mid = nums.subList(1, 4); // [20, 30, 40] — a view mid.set(0, 99); // writes through to parent // nums is now [10, 99, 30, 40, 50] ``` ## Idiomatic range operations Because the view shares storage, you can mutate a slice of the parent through it. The classic one: ``` nums.subList(1, 4).clear(); // removes positions 1..3 from nums ``` This is the standard way to delete a contiguous range from a list. ## When you actually want a copy If you want a snapshot that will not change when the parent changes (and that you can mutate independently), copy it: ``` List<Integer> copy = new ArrayList<>(nums.subList(1, 4)); ``` ## Important caveat (covered in depth elsewhere) The view is only valid as long as you do not **structurally modify** the parent through some other path. Doing so leaves the view in an undefined state and typically triggers a `ConcurrentModificationException` on the next view operation. Build/teardown the view in a small scope.

  • How do you get an independent copy of a sublist instead of a view?
    Wrap it in a new collection: new ArrayList<>(list.subList(from, to)). The constructor copies the elements, so the result no longer shares storage with the parent.
  • What does subList(2, 2) return?
    An empty list view. The range is half-open, so from == to means zero elements. It is valid as long as the indices are within bounds.

subList is like a window cut into a fence: you are not building a new fence, you are just looking at a section of the same boards. Paint a board through the window and it stays painted on the whole fence.

saying these in an interview costs you the question

  • Saying subList returns a copy of the elements
  • Thinking the range is inclusive on both ends (off-by-one)
  • Believing changes to the view do not affect the parent
  • Assuming subList is expensive for large ranges (it is O(1))

context