What is CopyOnWriteArrayList and how does it differ from a regular ArrayList in a multithreaded program?
answer
- Every write copies the whole array
- Reads are lock-free, never block
- volatile array reference, atomic swap
- Read-mostly, write-rarely use case
- Listener lists are the classic example
basics
~20 sCopyOnWriteArrayList is a thread-safe list. Every time you add or remove an element, it makes a fresh copy of the internal array. Reads happen on the array without locking, so many threads can read safely at the same time. A plain ArrayList is not thread-safe.
solid answer
~40 sCopyOnWriteArrayList is a thread-safe List implementation in java.util.concurrent. Its defining trait: every mutating operation (add, set, remove) copies the entire backing array, applies the change to the copy, then atomically swaps it in as the new array. Reads (get, iterate, size) never lock and operate on whatever array reference exists at that moment. By contrast, ArrayList is not synchronized at all: concurrent structural modification corrupts state or throws. The trade-off is that COW reads are lock-free and never block, but every write is O(n) because it duplicates the whole array. So it's tuned for read-mostly, rarely-written data such as listener lists, not for write-heavy workloads.
go deeper
Knows it is a thread-safe list where writes copy the array and reads don't lock; can name the read-mostly use case.
Explains the volatile reference and atomic swap, and the O(n)-per-write trade-off versus ArrayList.
Compares it against synchronizedList and ConcurrentHashMap-style alternatives, and reasons about when the snapshot semantics are an asset versus a liability.
Frames it within a concurrency strategy menu (immutability/COW vs fine-grained locking vs lock-free structures) and sets guidance on where it belongs in a codebase.
## The problem it solves A **List** is an ordered collection (like `[a, b, c]`) where each element has an index. Java's everyday `ArrayList` stores elements in a plain Java array internally and is **not thread-safe**: if two **threads** (independent paths of execution running concurrently) modify it at once — or one modifies while another reads — you can get corrupted data, lost updates, or a thrown exception. A field is *thread-safe* when concurrent access from multiple threads still produces correct results without the caller adding its own locking. ## What "copy on write" means `CopyOnWriteArrayList` (often abbreviated **COW**, in package `java.util.concurrent`) guarantees thread-safety with a specific strategy named in its title: - It keeps its elements in an internal array referenced by a single `volatile` field. `volatile` means: when one thread writes that reference, every other thread immediately sees the new value (no stale cached copy). - **Reads** — `get(i)`, `size()`, iteration — just read the current array. No lock is taken, so reads never block and never wait. - **Writes** — `add`, `set`, `remove` — take an internal lock, **make a brand-new copy of the entire array**, apply the change to that copy, then reassign the `volatile` reference to point at the new array. The old array is untouched and eventually garbage-collected. Because writers never mutate an array that readers might be looking at, readers can run with zero coordination and always see a *consistent* (if possibly slightly old) snapshot. ## Mental model Think of a shared whiteboard you may only **read**, never erase. To change it, you photocopy the whole board, edit the copy, and replace the original wholesale. Anyone mid-read of the old board finishes reading a complete, self-consistent board — they just won't see your edit until they look again. ## Concrete contrast | Aspect | `ArrayList` | `CopyOnWriteArrayList` | |---|---|---| | Thread-safe? | No | Yes | | Cost of a read | O(1) | O(1), lock-free | | Cost of one write | O(1) amortized | O(n) — copies whole array | | Iterator on concurrent change | throws `ConcurrentModificationException` | never throws; sees a snapshot | | Good for | single-threaded / externally synchronized | read-mostly, rarely-written shared data | ## Why not just use it everywhere? Because every single write duplicates the whole backing array, write-heavy use is extremely costly in both CPU and memory churn. COW is a *specialized* tool: it shines for lists that are read constantly but mutated occasionally — the textbook example is a list of event listeners that you iterate on every event but only modify when something subscribes or unsubscribes. There is a sibling, `CopyOnWriteArraySet`, that applies the same strategy to a `Set` (a collection with no duplicates), backed internally by a `CopyOnWriteArrayList`.
- Why is CopyOnWriteArrayList a poor choice for a write-heavy list?Each mutating call copies the entire backing array (O(n) time and a full extra allocation), so frequent writes cause severe CPU and garbage-collection overhead that scales with both list size and write rate.
- What does volatile give the backing-array reference here?Visibility and atomicity of the swap: when a writer reassigns the reference to the new array, readers immediately see the new reference rather than a stale cached one.
saying these in an interview costs you the question
- Saying it locks on reads — reads never lock
- Claiming writes are cheap — each write is O(n)
- Thinking it's a drop-in replacement for ArrayList everywhere
- Confusing it with Collections.synchronizedList (that one locks reads too and its iterator still throws)