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.
| Operation | ArrayList | LinkedList |
|---|---|---|
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)† |
| Iteration | Fast (cache-friendly) | Slower |
| Memory per element | Low | High (2 extra refs/node) |
* Amortized; occasional O(n) on resize. † O(n) to reach the index, O(1) to unlink.
Default to
ArrayList. ChooseLinkedListonly for queue/deque-style workloads at the ends — and even thenArrayDequeis 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.asListreturns a fixed-size view:setworks, butadd/removethrowUnsupportedOperationException. Wrap it innew ArrayList<>(...)when you need to grow.
Best Practices
- Prefer
ArrayList; reserveLinkedListfor genuine end-insertion workloads (or useArrayDeque). - Pre-size with
new ArrayList<>(capacity)when the element count is known. - Use
List.of(...)for immutable lists and to communicate intent. - Favor
removeIfand theIteratorover index-based loops when mutating during iteration. - Build
Comparators withcomparing/thenComparingrather than hand-writtencomparemethods.