Java

Copy-on-Write Collections

Master Copy-On-Write semantics for read-heavy concurrent scenarios. Understand when CopyOnWriteArrayList and CopyOnWriteArraySet shine, their memory implications, and why they're perfect for event listener patterns.

๐Ÿ“‹ At a Glance

AspectDetails
TopicCopyOnWriteArrayList, CopyOnWriteArraySet, snapshot iterators
ComplexityIntermediate
PrerequisitesPart 18 (ConcurrentHashMap), basic concurrency
Time to Master2-3 hours
Interview FrequencyMedium-High (listener patterns, read-heavy scenarios)

๐ŸŽฏ What You'll Learn

After completing this article, you will be able to:

  1. Understand Copy-On-Write semantics and memory implications
  2. Use CopyOnWriteArrayList for event listener management
  3. Know when COW collections are appropriate (and when not)
  4. Implement thread-safe observer patterns without locks
  5. Avoid common COW pitfalls like memory spikes

Production Story: The Event Listener That Never Threw

The Incident

Our UI framework was plagued by ConcurrentModificationException in the event dispatch system. Adding or removing listeners during event dispatch caused random crashes:
JAVA(19 lines)
Code
Loading syntax highlighter...

Why It Failed

TEXT(7 lines)
Code
Loading syntax highlighter...

The CopyOnWriteArrayList Solution

JAVA(19 lines)
Code
Loading syntax highlighter...

How It Works

TEXT(19 lines)
Code
Loading syntax highlighter...

Performance Trade-off

JAVA(8 lines)
Code
Loading syntax highlighter...

Mental Model: The Immutable Photograph

TEXT(29 lines)
Code
Loading syntax highlighter...

Interactive Visualization

Watch how CopyOnWrite handles concurrent readers and writers:

Loading visualizer...

Deep Dive: CopyOnWriteArrayList Internals

Core Structure

JAVA(12 lines)
Code
Loading syntax highlighter...

Write Operations (Always Copy)

JAVA(35 lines)
Code
Loading syntax highlighter...

Read Operations (No Copy, No Lock)

JAVA(35 lines)
Code
Loading syntax highlighter...

Deep Dive: CopyOnWriteArraySet

Implementation

JAVA(16 lines)
Code
Loading syntax highlighter...

addIfAbsent Pattern

JAVA(23 lines)
Code
Loading syntax highlighter...

Deep Dive: When to Use COW Collections

Perfect Use Cases

JAVA(15 lines)
Code
Loading syntax highlighter...

Bad Use Cases

JAVA(20 lines)
Code
Loading syntax highlighter...

Decision Matrix

TEXT(18 lines)
Code
Loading syntax highlighter...

Deep Dive: Common Patterns

Pattern 1: Event Dispatcher

JAVA(28 lines)
Code
Loading syntax highlighter...

Pattern 2: Configuration Registry

JAVA(30 lines)
Code
Loading syntax highlighter...

Pattern 3: Plugin Manager

JAVA(36 lines)
Code
Loading syntax highlighter...

Pattern 4: Rate Limiter Whitelist

JAVA(24 lines)
Code
Loading syntax highlighter...

Deep Dive: Memory Implications

Memory Spike During Updates

JAVA(24 lines)
Code
Loading syntax highlighter...

Memory Calculation

JAVA(15 lines)
Code
Loading syntax highlighter...

Garbage Collection Impact

JAVA(10 lines)
Code
Loading syntax highlighter...

โš ๏ธ Common Mistakes

Mistake 1: Using for Large/Frequently Modified Lists

JAVA(14 lines)
Code
Loading syntax highlighter...

Mistake 2: Expecting Iterator to See Updates

JAVA(13 lines)
Code
Loading syntax highlighter...

Mistake 3: Modifying Through Iterator

JAVA(14 lines)
Code
Loading syntax highlighter...

Mistake 4: Using contains() on Large Sets

JAVA(14 lines)
Code
Loading syntax highlighter...

Mistake 5: Not Using addAll for Batch Operations

JAVA(8 lines)
Code
Loading syntax highlighter...

๐Ÿ› Debug This

Challenge 1: The Missing Element

JAVA(15 lines)
Code
Loading syntax highlighter...
โœ… Answer:

Output:

TEXT(3 lines)
Code
Loading syntax highlighter...
The iterator sees a snapshot [A, B] taken at iteration start. "C" is added after, so it's not in the snapshot. But size() sees the current list, so it shows 3.

Challenge 2: The Duplicate Prevention

JAVA(10 lines)
Code
Loading syntax highlighter...
โœ… Answer:
Output: 1
CopyOnWriteArraySet uses addIfAbsent which is atomic. Only one "X" will be added regardless of race condition.

Challenge 3: The Memory Spike

JAVA(6 lines)
Code
Loading syntax highlighter...
โœ… Answer:
Peak memory is approximately 2MB at the end.
Each add() copies the array. At iteration 999:
  • Old array: 999 KB (999 references)
  • New array: 1000 KB (1000 references)
  • Actual byte arrays: 1000 KB

Total peak: ~2MB for array references, plus ~1MB for actual byte arrays = ~3MB

If using addAll() with pre-built list, peak would be ~1.5MB.

๐Ÿ’ป Exercises

Exercise 1: Observable Value

Implement a thread-safe observable value:

JAVA(6 lines)
Code
Loading syntax highlighter...
โœ… Solution:
JAVA(31 lines)
Code
Loading syntax highlighter...

Exercise 2: Feature Flag Service

Implement a thread-safe feature flag service:

JAVA(6 lines)
Code
Loading syntax highlighter...
โœ… Solution:
JAVA(26 lines)
Code
Loading syntax highlighter...

Exercise 3: Event Bus with Topics

Implement a simple event bus with topic subscription:

JAVA(5 lines)
Code
Loading syntax highlighter...
โœ… Solution:
JAVA(32 lines)
Code
Loading syntax highlighter...

๐ŸŽค Senior-Level Interview Questions

Question 1: COW vs Synchronized Collections

Q: When would you use CopyOnWriteArrayList vs Collections.synchronizedList?
A:
AspectCopyOnWriteArrayListsynchronizedList
Read performanceO(1), no lockO(1), acquires lock
Write performanceO(n), copies arrayO(1), acquires lock
Iteration safetyNever throws CMECan throw CME
Memory2x during writesConstant
Best forRead-heavy, few writesBalanced read/write
JAVA(5 lines)
Code
Loading syntax highlighter...

Question 2: Snapshot Iterator Semantics

Q: Explain what "snapshot iterator" means and its implications.
A:

Snapshot iterator captures array reference at creation:

JAVA(15 lines)
Code
Loading syntax highlighter...

Question 3: Memory Implications

Q: What are the memory implications of CopyOnWriteArrayList?
A:
  1. Peak memory during write: 2x normal (old + new array)
  2. GC pressure: Old arrays become garbage after writes
  3. Temporary arrays: Each write creates a new array
JAVA(8 lines)
Code
Loading syntax highlighter...

Question 4: CopyOnWriteArraySet contains() Complexity

Q: What's the time complexity of contains() in CopyOnWriteArraySet?
A:
O(n) - linear search!
JAVA(10 lines)
Code
Loading syntax highlighter...

Question 5: Safe Publication

Q: How does CopyOnWriteArrayList ensure safe publication of new arrays?
A:
Uses volatile for the array reference:
JAVA(12 lines)
Code
Loading syntax highlighter...

๐Ÿ“ Summary & Key Takeaways

Copy-On-Write Semantics

  • Write operations create new array copy
  • Read operations use current array (no copy)
  • Iterators see snapshot at creation time
  • Never throws ConcurrentModificationException

When to Use

  • Few writes, many reads
  • Event listener patterns
  • Small lists/sets (<100-1000 elements)
  • Need safe concurrent iteration

When NOT to Use

  • Large collections (memory spikes)
  • Frequent writes (O(n) each)
  • contains-heavy operations (O(n) for Set)
  • Size-sensitive applications

Performance Characteristics

  • Read: O(1), no locking
  • Write: O(n), creates copy
  • Iteration: O(n), snapshot
  • Memory: 2x peak during writes

๐Ÿ Conclusion

Copy-On-Write collections solve a specific problem elegantly: safe concurrent iteration without locking. They're perfect for event listeners, configuration caches, and other read-heavy scenarios. But they come with trade-offs in memory and write performance that must be understood.

Key takeaways:

  1. Perfect for listener patterns - few registrations, many notifications
  2. Snapshot iterators never fail - but don't see concurrent modifications
  3. Memory doubles during writes - batch updates with addAll()
  4. O(n) contains for Set - use ConcurrentHashMap.newKeySet() for large sets
  5. Keep collections small - COW doesn't scale for large data

In the next article, we'll explore BlockingQueues - the foundation of producer-consumer patterns and thread coordination in concurrent applications.


๐Ÿ“… Review Schedule

To solidify your understanding, review this material:

  • Tomorrow: Implement an event dispatcher with COW
  • In 3 days: Compare COW vs synchronized for your use cases
  • In 1 week: Review memory implications and batch update patterns
  • In 2 weeks: Practice choosing between COW and alternatives