skip to content

subList Views & indexOf

subList returns a live view over the parent, so changes flow both ways and structural changes to the parent invalidate it. indexOf and contains rely on the element's equals, which ties list search back to the equality contract.

part ofJavaoverview, primer and where to startread it →
on this pageshow

questions

5

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

open as a page

How do List.indexOf and List.contains decide whether an element matches, and why does overriding equals matter?

level: middleimportance: must knowfreq 62%

basics

~20 s

indexOf and contains find an element by comparing with equals, not by reference (==). For a custom class to be found, it must override equals correctly; otherwise the default equals compares object identity and lookups fail unless it is the exact same object.

open as a page

Why can a subList view throw ConcurrentModificationException, and how do you avoid it?

level: middleimportance: must knowfreq 48%

basics

~20 s

A subList view becomes invalid if you structurally change the original list (add or remove elements) through some other reference. The next time you use the view, it usually throws ConcurrentModificationException. Avoid it by using the view only within a short scope.

open as a page

When is using List.contains/indexOf for membership testing the wrong choice, and what should you use instead?

level: seniorimportance: should knowfreq 40%

basics

~10 s

List.contains/indexOf scan the whole list, so each check is O(n). If you do many membership checks, that becomes slow (O(n*m)). Use a HashSet instead, which checks membership in roughly O(1) on average.

open as a page

How do you delete a contiguous range from an ArrayList efficiently, and why is subList(...).clear() the idiomatic approach?

level: seniorimportance: should knowfreq 35%

basics

~10 s

Use list.subList(from, to).clear(). Because the sublist is a view of the original, clearing it removes that whole range from the original list in one operation, instead of removing elements one at a time.

open as a page