Java List Interface
Ordered, index-accessible collections in Java with the List interface and its core operations.
Java List Interface
List<E> is Collection<E> plus two extra commitments: the elements have a defined order, and that order is addressable by integer index. Once you have order and an index, a whole class of methods becomes meaningful — get(i), set(i, x), indexOf(x), subList(from, to), sort, iterate in reverse. This chapter walks the contract; the implementations (ArrayList, LinkedList, Vector) come immediately afterwards with their own performance trade-offs.
What "ordered" means here
"Ordered" on a List means insertion order is preserved — index 0 is the first element you put in, index size() - 1 is the last, and adding a new element at the end shifts nothing. It is not "sorted" — a list keeps whatever order you produce. If you want sorted iteration, you either call Collections.sort(list) (which mutates), or use TreeSet / TreeMap from the start. Don't conflate the two.
Duplicates are allowed. [1, 1, 2, 1] is a perfectly legal List<Integer>.
The methods List adds on top of Collection
Everything Collection declares is still there — add, remove, contains, size, etc. List then adds positional and order-aware operations:
Positional access
E get(int index)— element atindex.E set(int index, E element)— replace, returning the old value.void add(int index, E element)— insert (shifts later elements right).E remove(int index)— remove by position (returns the removed element). Note the overload withObject—list.remove(1)calls theintversion;list.remove(Integer.valueOf(1))calls theObjectversion.
Search
int indexOf(Object o)— first occurrence, or-1.int lastIndexOf(Object o)— last occurrence, or-1.
Sub-views and iteration
List<E> subList(int fromIndex, int toIndex)— a live view of a range. Modifying it modifies the backing list (and vice versa). Half-open:[from, to).ListIterator<E> listIterator()/listIterator(int index)— iterator that can also walk backwards, get the current index, andset/addat the cursor. The ListIterator chapter covers it.
Bulk mutation tied to order
default void replaceAll(UnaryOperator<E> op)— applyopto every element in place.default void sort(Comparator<? super E> c)— sort the list usingc(or natural order ifnull).boolean addAll(int index, Collection<? extends E> c)— insert a whole collection atindex.
Factories (Java 9+)
List.of(...)— an unmodifiable list of the given elements. Compact, allocation-free for tiny sizes.List.copyOf(Collection)— an unmodifiable snapshot of another collection.
Equality on List is order-sensitive
Two lists are equal iff they have the same size, in the same order, with equal elements at every index. List.of(1, 2) does not equal List.of(2, 1), even though as Sets they would. That's a hard rule from the List contract — if you find yourself comparing two lists and getting false when you "shouldn't," check the order first.
subList is a view, not a copy
This trips up almost every learner once:
List<Integer> xs = new ArrayList<>(List.of(0, 1, 2, 3, 4, 5));
List<Integer> middle = xs.subList(2, 5); // [2, 3, 4]
middle.set(0, 99);
System.out.println(xs); // [0, 1, 99, 3, 4, 5] — xs changed!
middle.clear();
System.out.println(xs); // [0, 1, 5] — gone from xssubList returns a live window. Reads and writes go through to the backing list. That's hugely useful for in-place algorithms — clear a range, sort a range, insert a range — but it also means you can't keep a subList reference around and then mutate the parent through any other path. The Javadoc says structural changes to the backing list outside the sub-list "undefine" the sub-list's behaviour. In practice, ConcurrentModificationException on the very next call.
If you want an independent slice, copy it: new ArrayList<>(xs.subList(2, 5)).
The two remove overloads
A common bug:
List<Integer> nums = new ArrayList<>(List.of(10, 20, 30));
nums.remove(1); // removes index 1 → [10, 30]
nums.remove(Integer.valueOf(10)); // removes the value 10 → [30]The int overload wins because int is more specific than Integer. If you mean "remove the value 10," box it explicitly. This is one of the few places in the language where the autoboxing rules and overload resolution actively conflict.
Sorting in place
list.sort(comparator) mutates the list. Pass null to use the elements' natural order (their Comparable); pass a Comparator otherwise. This is the modern form — Collections.sort(list) still works and is identical, but list.sort(...) is the default List method:
List<String> names = new ArrayList<>(List.of("Linus", "Ada", "Grace"));
names.sort(null); // natural: ["Ada", "Grace", "Linus"]
names.sort(Comparator.comparingInt(String::length)); // shortest firstThe Comparable / Comparator chapter later in this part is the rulebook for what null means and how to build comparators for your own types.
Immutable factories: when add throws
List.of(...), List.copyOf(...), and the lists returned by Collectors.toUnmodifiableList() are unmodifiable. They reject every mutating call with UnsupportedOperationException. They also reject null elements. They're ideal for read-only data shared widely:
List<String> CONSTANTS = List.of("red", "green", "blue");
CONSTANTS.add("yellow"); // throws UnsupportedOperationExceptionIf you might want to mutate later, start with new ArrayList<>(List.of(...)).
A worked example: every List-specific method
The program below exercises the methods List adds beyond Collection. Watch the subList mutation propagate, the overload trap, and the difference between sort and replaceAll.
A few takeaways from the output you should hold onto:
remove(1)removed20(the value at index 1);remove(Integer.valueOf(10))removed10by value. Same method name, two different jobs based on the static type of the argument.- After
mid.clear(), the parent list is[0, 1, 5]. The view was the range — clearing it removed those elements from the backing array. replaceAllkeeps the list the same length and rewrites each element in place;sortrearranges what's already there. They compose well.
What's next
You now know the List contract — what's guaranteed, what mutates what, where the foot-guns are. Time to meet the implementation you'll use 90% of the time: the resizable-array-backed ArrayList. Same contract, specific performance characteristics, and a few extras of its own.
Practice
`xs` is `new ArrayList<>(List.of(10, 20, 30, 40))`. You call `xs.subList(1, 3).clear()`. What is `xs` afterwards?