CopyOnWriteArrayList replaces its backing array for updates and gives an iterator the array state present when that iterator was created.
Java CopyOnWriteArrayList: listener snapshots and write amplification
Java 8+. This is a complete program using JDK classes.
Choose a stable audience
A notification dispatcher needs to visit the subscribers that existed when delivery began. A later registration should affect the next delivery, not interrupt the current traversal. Snapshot iteration fits that rule without locking the dispatch loop across every callback.
The snapshot freezes the sequence of references. It does not freeze the subscriber objects. If a subscriber changes its own fields, the iterator still holds that same object. Keep registration changes and listener-state coordination as separate responsibilities.
The write tradeoff is visible even without a timer: an update copies array storage instead of changing one slot in place. This is a plausible choice for a small listener list with rare changes, and an expensive choice for a large queue that receives a write per message.
Review the iterator contract
The old cursor in the program prints the first audience; a fresh list rendering prints the changed audience. Its remove method is rejected. An algorithm designed around Iterator.remove must therefore choose another collection or collect a separate set of removals and apply them through the list after traversal.
Working program
import java.util.Arrays;
import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;
public class SubscriberSnapshot {
public static void main(String[] args) {
CopyOnWriteArrayList<String> listeners = new CopyOnWriteArrayList<>(Arrays.asList("email", "audit"));
Iterator<String> before = listeners.iterator();
listeners.add("metrics");
while (before.hasNext()) System.out.println(before.next());
System.out.println(listeners);
try { before.remove(); }
catch (UnsupportedOperationException rejected) { System.out.println("Snapshot cannot remove"); }
}
}Output
email
audit
[email, audit, metrics]
Snapshot cannot removeCosts and boundaries
Traversal of n listeners is O(n). Ordinary element-changing updates can copy O(n) array entries. Existing iterators can retain older arrays, so several long-lived snapshots can increase retained storage. Subscriber objects may be shared between arrays; a list copy is not a deep object copy.
Common Mistakes
- Do not use this as a default high-write work queue.
- A snapshot can include a listener removed after its creation.
- Thread-safe list operations do not make callbacks thread-safe.
