Copy-on-Write Collections
๐ Quick Reference
| Property | Value |
|---|---|
| Type | Thread-safe collections via copy-on-write |
| Read | O(1) - no locking |
| Write | O(n) - copies entire array |
| Iteration | Safe, snapshot at iterator creation |
| Best For | Read-heavy, rare writes, event listeners |
๐ฎ Interactive Visualizer
Watch how Copy-on-Write handles concurrent reads and writes:
Loading visualizer...
- See multiple readers access simultaneously (no blocking)
- Watch a write operation create a new copy
- Observe iterators using the old snapshot
- Notice: readers never blocked, even during writes
๐ง Key Operations
CopyOnWriteArrayList
JAVA(24 lines)CodeLoading syntax highlighter...
CopyOnWriteArraySet
JAVA(10 lines)CodeLoading syntax highlighter...
๐ Complexity Analysis
| Operation | CopyOnWriteArrayList | CopyOnWriteArraySet |
|---|---|---|
get(i) | O(1) | N/A |
contains | O(n) | O(n) |
add | O(n) | O(n) |
remove | O(n) | O(n) |
iterator() | O(1) | O(1) |
| Iteration | O(n) | O(n) |
How Copy-on-Write Works
Initial state: reference โ [A, B, C, D] Write operation (add "E"): 1. Create new array: [A, B, C, D, E] 2. Copy existing elements 3. Add new element 4. Atomically swap reference reference โ [A, B, C, D, E] (new array) [A, B, C, D] (old array, still used by existing iterators) Existing iterators continue using old array (snapshot) New iterators use new array
โ When to Use Copy-on-Write
Good Use Cases
- Event listeners - listener list rarely changes, iterated often
- Configuration - read constantly, updated rarely
- Observer pattern - notify many observers, add/remove rare
- Snapshot iteration - need consistent view during traversal
- Read-heavy workloads - writes are 1% or less of operations
Avoid When
- Write-heavy - each write copies entire array
- Large collections - copy cost too high
- Memory constrained - maintains old copies until iterators complete
- Need O(1) contains - use ConcurrentHashMap.newKeySet() instead
๐ Copy-on-Write vs Alternatives
| Feature | CopyOnWriteArrayList | synchronizedList | ConcurrentLinkedQueue |
|---|---|---|---|
| Iteration | Safe (snapshot) | Needs external sync | Safe (weakly consistent) |
| Read performance | Excellent | Good | Good |
| Write performance | Poor (O(n)) | Good (O(1)) | Good (O(1)) |
| Memory | Higher (copies) | Normal | Normal |
| Best for | Read-heavy | Moderate mix | Queue operations |
JAVA(8 lines)CodeLoading syntax highlighter...
๐งฉ Common Patterns
Event Listener Management
JAVA(20 lines)CodeLoading syntax highlighter...
Configuration Registry
JAVA(15 lines)CodeLoading syntax highlighter...
Observer Pattern
JAVA(14 lines)CodeLoading syntax highlighter...
โ ๏ธ Common Pitfalls
1. Using for Write-Heavy Workloads
JAVA(8 lines)CodeLoading syntax highlighter...
2. Expecting CopyOnWriteArraySet O(1) Contains
JAVA(6 lines)CodeLoading syntax highlighter...
3. Large Collections
JAVA(6 lines)CodeLoading syntax highlighter...
4. Modifying During Iteration
JAVA(16 lines)CodeLoading syntax highlighter...
๐ค Interview Tips
"When you have many more reads than writes, especially for things like event listeners or configuration. Writes are expensive (O(n)) but reads are lock-free and iteration is always safe.
"Every mutative operation (add, set, remove) creates a fresh copy of the underlying array. The reference is then atomically swapped. Reads see a consistent snapshot without locking.
"The iterator works on a snapshot of the array at creation time. The current array may have changed since then, so the iterator can't know which element to remove from the current array.
"CopyOnWriteArraySet has O(n) contains (linear search) and O(n) writes. ConcurrentHashMap.newKeySet() has O(1) contains and O(1) writes. Use COW only if you need snapshot iteration or very small sets.
๐ Series Navigation
CopyOnWrite from @tomaszjarosz/react-visualizers