Skip to content
Dev Dump

List Interface

The List interface represents an ordered sequence of elements. Elements maintain insertion order, support index-based access, and duplicates are allowed.

  • Ordered — elements stay in the order you insert them
  • Indexed — access any element by position: list.get(2)
  • Duplicates allowed[a, b, a] is valid
  • Null allowed — in most implementations
OperationArrayListLinkedList
Random access get(i)O(1)O(n)
Insert at endO(1)*O(1)
Insert at beginningO(n)O(1)
Insert at middleO(n)O(n)**
Delete at endO(1)O(1)
Delete at beginningO(n)O(1)
Search contains()O(n)O(n)
Memory per elementLowHigh (prev/next pointers)
Thread-safeNoNo

*Amortized — O(n) during resize. **O(1) if you already have a reference to the node via Iterator.

List<String> list = new ArrayList<>();
list.add("Apple"); // append
list.add(1, "Orange"); // insert at index
list.set(0, "Mango"); // replace at index
String item = list.get(0); // access by index
int idx = list.indexOf("Mango"); // first occurrence
list.remove(0); // remove by index
list.remove("Orange"); // remove first occurrence of object
List<String> sub = list.subList(0, 2); // view, not a copy
list.sort(Comparator.naturalOrder());
list.replaceAll(String::toUpperCase);

The go-to List implementation for most use cases. Backed by a dynamic array.

ArrayList<String> list = new ArrayList<>(); // default capacity 10
ArrayList<String> list = new ArrayList<>(100); // pre-size if you know the count
ArrayList<String> list = new ArrayList<>(existing); // copy from another collection

When to use: Almost always. Random access is O(1), appending is fast, and memory overhead is low. This should be your default unless you have a specific reason for LinkedList.

Resizing: When the internal array fills up, ArrayList allocates a new array ~1.5x the size and copies everything over. Pre-sizing avoids this if you know the approximate count.

A doubly-linked list that also implements Deque. Each element is a separate node with prev/next pointers.

LinkedList<String> list = new LinkedList<>();
// Deque methods — O(1) at both ends
list.addFirst("start");
list.addLast("end");
list.removeFirst();
list.removeLast();
// Also works as a Queue
list.offer("item"); // add to tail
list.poll(); // remove from head
list.peek(); // look at head

When to use: Only when you frequently insert/remove at the beginning of the list, or when you need Queue/Deque behavior. For almost everything else, ArrayList is faster in practice due to cache locality.

Avoid: list.get(500) on a LinkedList — it traverses from the head, making indexed access O(n).

// Java 9+ — truly immutable, throws UnsupportedOperationException on mutation
List<String> immutable = List.of("a", "b", "c");
// Arrays.asList — fixed-size but elements are mutable
List<String> fixed = Arrays.asList("a", "b", "c");
// fixed.add("d"); // throws UnsupportedOperationException
// fixed.set(0, "x"); // works fine
// Unmodifiable wrapper
List<String> unmod = Collections.unmodifiableList(mutableList);
ScenarioUse
General-purpose listArrayList
Frequent random accessArrayList
Frequent insert/remove at headLinkedList
Need Queue or Deque behaviorLinkedList or ArrayDeque
Thread-safe listCollections.synchronizedList() or CopyOnWriteArrayList
Immutable listList.of(...)