Skip to content
Java core 2 min read

List

A List<E> is an ordered, index-addressable collection that permits duplicates. It is the workhorse of the Collections Framework: whenever you need a sequence whose order you control, you reach for a List. The two implementations you will use are ArrayList (a resizable array) and LinkedList (a doubly linked list).

ArrayList vs LinkedList

ArrayList backs the list with a contiguous array — excellent for random access and iteration, with occasional O(n) resize copies. LinkedList stores each element in a node holding prev/next pointers — cheap end insertions but pointer-chasing access and poor cache locality.

OperationArrayListLinkedList
get(i) / set(i)O(1)O(n)
add(e) (append)O(1)*O(1)
add(0, e) (prepend)O(n)O(1)
remove(i) (middle)O(n)O(n)†
IterationFast (cache-friendly)Slower
Memory per elementLowHigh (2 extra refs/node)

* Amortized; occasional O(n) on resize. † O(n) to reach the index, O(1) to unlink.

Default to ArrayList. Choose LinkedList only for queue/deque-style workloads at the ends — and even then ArrayDeque is usually faster.

Common Operations

List<String> langs = new ArrayList<>();
langs.add("Java");
langs.add("Kotlin");
langs.add(1, "Scala");          // insert at index
langs.set(0, "Java 21");         // replace
langs.remove("Scala");           // remove by value
boolean has = langs.contains("Kotlin");
System.out.println(langs + " contains Kotlin? " + has);

Output:

[Java 21, Kotlin] contains Kotlin? true

Iteration

Three idioms, from most to least common:

List<Integer> xs = List.of(10, 20, 30);

for (int x : xs) { /* enhanced for — preferred */ }

xs.forEach(x -> System.out.print(x + " "));   // 10 20 30

for (int i = 0; i < xs.size(); i++) {          // when you need the index
    System.out.print(i + "=" + xs.get(i) + " ");
}

To remove while iterating, use the iterator directly or removeIf to avoid ConcurrentModificationException:

List<Integer> nums = new ArrayList<>(List.of(1, 2, 3, 4));
nums.removeIf(n -> n % 2 == 0);   // [1, 3]

Sorting

Implement Comparable<T> for a type’s natural order; pass a Comparator<T> for ad-hoc orders.

record User(String name, int age) {}

List<User> users = new ArrayList<>(List.of(
    new User("Ada", 36), new User("Linus", 24), new User("Grace", 36)));

users.sort(Comparator.comparingInt(User::age)
                     .thenComparing(User::name));

users.forEach(u -> System.out.println(u.name() + " " + u.age()));

Output:

Linus 24
Ada 36
Grace 36

Comparator is fluent: chain thenComparing, flip with reversed(), and guard nulls with nullsFirst/nullsLast. For natural order on a Comparable type, call list.sort(null) or Collections.sort(list).

Arrays ↔ Lists

String[] arr = {"a", "b", "c"};
List<String> fixed = Arrays.asList(arr);          // fixed-size, backed by arr
List<String> grow = new ArrayList<>(List.of(arr)); // independent, mutable

String[] back = grow.toArray(new String[0]);       // List -> array
int[] prims = List.of(1, 2, 3).stream().mapToInt(Integer::intValue).toArray();

Arrays.asList returns a fixed-size view: set works, but add/remove throw UnsupportedOperationException. Wrap it in new ArrayList<>(...) when you need to grow.

Best Practices

  • Prefer ArrayList; reserve LinkedList for genuine end-insertion workloads (or use ArrayDeque).
  • Pre-size with new ArrayList<>(capacity) when the element count is known.
  • Use List.of(...) for immutable lists and to communicate intent.
  • Favor removeIf and the Iterator over index-based loops when mutating during iteration.
  • Build Comparators with comparing/thenComparing rather than hand-written compare methods.
Last updated June 1, 2026
Was this helpful?