collections

CopyOnWriteArrayList vs ArrayList

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

While ArrayList is the standard dynamic array in Java, it fails spectacularly in highly concurrent environments, throwing ConcurrentModificationException when threads modify it during iteration. CopyOnWriteArrayList was introduced in Java 5 (java.util.concurrent) as an ingenious, lock-free alternative for very specific multi-threaded scenarios.


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

ArrayList is an unsynchronized, fast dynamic array for single-threaded use, whereas CopyOnWriteArrayList is thread-safe because it creates a completely fresh copy of the internal array every time it is modified, allowing 100% lock-free reads.


šŸ“Š 2. Detailed Comparison

FeatureArrayListCopyOnWriteArrayList
Thread SafetyāŒ Not thread-safeāœ… Completely thread-safe
Internal MechanismResizes existing array when fullCopies entire array on every write (add, remove, set)
Read PerformanceExtremely Fast (O(1))Extremely Fast (O(1)) and Lock-Free
Write PerformanceFast (Amortized O(1))Very Slow (O(N)) due to array copying
Memory OverheadLowVery High during modifications
Iterator BehaviorFail-FastFail-Safe (Iterates over a snapshot)
Iterator remove()āœ… SupportedāŒ Throws UnsupportedOperationException

šŸ“Œ 3. How CopyOnWriteArrayList Works

The genius of CopyOnWriteArrayList lies in its name: Copy-On-Write.

šŸ”ø The Write Operation

Whenever a thread calls a mutative operation like add(E e) or remove(int index):

  1. It acquires a lock (using ReentrantLock).
  2. It creates an exact copy of the internal array, but with a length of N + 1 (or N - 1).
  3. It performs the modification on the new array.
  4. It swaps the internal array reference to point to the new array.
  5. It releases the lock.
// Simplified internal logic of CopyOnWriteArrayList.add() public boolean add(E e) { final ReentrantLock lock = this.lock; lock.lock(); try { Object[] elements = getArray(); int len = elements.length; // CREATE FRESH COPY Object[] newElements = Arrays.copyOf(elements, len + 1); newElements[len] = e; // SWAP REFERENCE setArray(newElements); return true; } finally { lock.unlock(); } }

šŸ‘‰ The Impact: Every single write is an expensive O(N) operation.


šŸ”ø The Read Operation

Because the internal array reference is only swapped after a complete copy is made, the array itself is effectively immutable. This means read operations like get(), contains(), or iterating do not need any locks whatsoever.


šŸ“Œ 4. The Snapshot Iterator (Fail-Safe)

When you request an Iterator from an ArrayList, it tracks a modCount. If the list changes, the iterator crashes (Fail-Fast).

When you request an Iterator from a CopyOnWriteArrayList, it takes a reference to the exact array that existed at that exact millisecond (a snapshot).

  • If another thread adds an element, the reference in the main list is swapped to a new array.
  • However, your Iterator is still safely looping over the old snapshot array.
  • It will never throw ConcurrentModificationException.
  • It will not see the new elements added after the iterator was created.

šŸ“Œ 5. Why Iterator.remove() throws an Exception

If you try to call remove() on a CopyOnWriteArrayList Iterator, it will crash with UnsupportedOperationException.

Why? Because the Iterator is operating on a stagnant "snapshot" of an old array. If you removed an element from the snapshot, it wouldn't affect the actual current list. To avoid logical corruption and ambiguity, Java explicitly bans modifications via the iterator.


šŸ“Œ 6. When to use which?

āœ… Use ArrayList when:

  • You are operating in a single-threaded environment.
  • You need to perform frequent writes/updates.
  • You need to synchronize the list externally using Collections.synchronizedList() (though rare today).

āœ… Use CopyOnWriteArrayList when:

  • Reads vastly outnumber writes. (e.g., 99% reads, 1% writes).
  • You are maintaining a list of Event Listeners or Observers. (Listeners are registered once and rarely removed, but notified/iterated over constantly).
  • You are building high-concurrency caches where iteration must be lightning fast and completely unblocked by sporadic updates.

šŸ”„ Interview Gold Statement

"CopyOnWriteArrayList provides lock-free, ultra-fast reads in concurrent environments by taking the radical approach of cloning the entire underlying array on every single write operation. Because iterators work on an immutable snapshot of the array, they never throw ConcurrentModificationException. However, this O(N) write cost makes it suitable strictly for scenarios where reads vastly outnumber writes, such as maintaining a list of event listeners."


⚔ Final Verdict

  • āœ… Use ArrayList as your standard, everyday dynamic array.
  • šŸŽÆ Use CopyOnWriteArrayList exclusively in multi-threaded scenarios where you iterate constantly but modify rarely.
CopyOnWriteArrayList vs ArrayList | DevExCode