collections

LinkedList vs ArrayList: When to use which?

šŸ”¹ LinkedList vs ArrayList (Deep Dive, Interview-Ready)

Both ArrayList and LinkedList are implementations of the List interface in Java, but they use entirely different underlying data structures. Understanding these differences is crucial for writing efficient code and acing technical interviews.


šŸ“Œ 1. Core Difference (One-Liner)

ArrayList uses a dynamic array for contiguous memory storage, making reads extremely fast, whereas LinkedList uses a doubly-linked list, making insertions and deletions fast but reads slower.


šŸ“Š 2. Detailed Comparison

FeatureArrayListLinkedList
Underlying Data StructureResizable Dynamic ArrayDoubly Linked List
Memory AllocationContiguous (Continuous memory block)Non-contiguous (Nodes scattered in heap)
Read Access (get())Extremely Fast O(1)Slower O(N) (Must traverse from head/tail)
Insertion/Deletion (add(), remove())Slower O(N) (Requires shifting elements)Faster O(1) (Just update pointers, assuming position is known)
Memory OverheadLow (Only stores data)High (Stores data + next/prev pointers)
CPU Cache LocalityExcellent (Array elements are pre-fetched)Poor (Pointer chasing causes cache misses)
Interfaces ImplementedList, RandomAccess, Cloneable, SerializableList, Deque, Queue, Cloneable, Serializable

šŸ“Œ 3. Internal Working Differences

šŸ”ø ArrayList

Internal Storage:

transient Object[] elementData;

How it works:

  1. Elements are stored next to each other in memory.
  2. When the array is full, it creates a new array of size oldCapacity + (oldCapacity >> 1) (which is 1.5x larger) and copies old elements into it.

šŸ‘‰ Optimized for:

  • Fast Random Access: list.get(5) calculates the exact memory address instantly.

šŸ”ø LinkedList

Internal Storage:

transient int size = 0; transient Node<E> first; transient Node<E> last; private static class Node<E> { E item; Node<E> next; Node<E> prev; }

How it works:

  1. Every element is wrapped in a Node object.
  2. Each node holds the data, a pointer to the next node, and a pointer to the previous node.

šŸ‘‰ Optimized for:

  • Frequent modifications: Inserting a node in the middle just requires updating the next and prev references of surrounding nodes. No array shifting is needed.

šŸ“Œ 4. Time Complexity Breakdown

OperationArrayListLinkedListReason
get(index)O(1)O(N)ArrayList computes the memory address directly. LinkedList must traverse node by node.
add(element) (at end)O(1) AmortizedO(1)Both add to the end instantly (ArrayList has rare O(N) resizes).
add(index, element)O(N)O(N)ArrayList must shift elements right. LinkedList must traverse O(N) to find the index, then inserts in O(1).
remove(index)O(N)O(N)ArrayList shifts elements left. LinkedList traverses to find it, then removes in O(1).
Iterator.remove()O(N)O(1)If already traversing, LinkedList removes instantly without shifting!

šŸ“Œ 5. The "CPU Cache" Trap (Why ArrayList almost always wins)

In theory, LinkedList should be faster at inserting into the middle of a list. In reality, ArrayList often beats LinkedList even for insertions.

Why? CPU Cache Locality.

  • Modern CPUs fetch data from RAM in "cache lines" (chunks of contiguous memory).
  • Because ArrayList uses contiguous memory, the CPU grabs a chunk of the array into its ultra-fast L1/L2 cache. Shifting elements in an array is essentially a lightning-fast System.arraycopy() memory block move.
  • LinkedList nodes are scattered randomly across the heap. Traversing them causes Cache Misses, forcing the CPU to repeatedly fetch data from slow RAM.

šŸ“Œ 6. When to use which?

āœ… Use ArrayList when:

  • You have frequent read/access operations (get(index)).
  • You mostly append elements to the end of the list.
  • Memory overhead is a concern (you don't want to create millions of Node objects).
  • Rule of Thumb: Default to ArrayList for 99% of your use cases.

āœ… Use LinkedList when:

  • You are implementing a Queue or Deque (since it implements the Deque interface).
  • You are using an Iterator to frequently remove or insert elements during traversal.
  • You are adding/removing elements from the extreme ends (Head/Tail) constantly.

šŸ”„ Interview Gold Statement

"While LinkedList theoretically provides O(1) insertions, it is rarely used in modern Java because traversing to the insertion index still takes O(N), and it suffers from poor CPU cache locality and high memory overhead due to Node object wrappers. ArrayList is vastly superior in performance for almost all real-world scenarios due to contiguous memory allocation."


⚔ Final Verdict

  • āœ… Use ArrayList as your absolute default.
  • āŒ Avoid LinkedList unless you strictly need a Queue/Deque implementation.
LinkedList vs ArrayList: When to use which? | DevExCode