Shirgill Farhan Ansari Shirgill Farhan Ansari - 2 months ago 18
Java Question

Iterators for the copy-on-write collections

An abstract from JCIP


Iterators for the copy-on-write collections retain a reference to the
backing array that was current at the start of iteration, and since
this will never change, they need to synchronize only briefly to
ensure visibility of the array contents.

The iterators returned by the copy-on-write collections do not throw
ConcurrentModificationException and return the elements exactly as
they were at the time the iterator was created, regardless of
subsequent modifications



Looking at the source code at #CopyOnWriteArrayList.iterator()

956 public Iterator<E> iterator() {
957 return new COWIterator<E>(getArray(), 0); // call to private constructor
958 }

993 private final Object[] snapshot;

994
995 private int cursor;
996
997 private COWIterator(Object[] elements, int initialCursor) {
998 cursor = initialCursor;
999 snapshot = elements;
1000 }


You see that the
snapshot
points to the array returned by
getArray()
and since the array reference returned is volatile, any change on the reference variable is guaranteed to be reflected. (Making the array reference volatile doesn't makes the elements at each index location volatile)

where the change on the array is done inside-

386 public E More ...set(int index, E element) {
387 final ReentrantLock lock = this.lock;
388 lock.lock();
389 try {
390 Object[] elements = getArray();
391 E oldValue = get(elements, index);
392
393 if (oldValue != element) {
394 int len = elements.length;
395 Object[] newElements = Arrays.copyOf(elements, len);
396 newElements[index] = element;
397 setArray(newElements);
398 } else {
399 // Not quite a no-op; ensures volatile write semantics
400 setArray(elements);
401 }
402 return oldValue;
403 } finally {
404 lock.unlock();
405 }
406 }


i.e
setArray(newElements);
as seen in the method.

where getArray()

92 final Object[] More ...getArray() {
93 return array;
94 }


& setArray(...)

final void More ...setArray(Object[] a) {
100 array = a;
101 }


are operations on this volatile array

private volatile transient Object[] array;


Clearly, the iterator is returning the array which (if) has been modified, and not the one created at the start of the iteration process.

So, what does the author mean-


return the elements exactly as they were at the time the iterator was
created
, regardless of subsequent modifications.

Answer

since the array returned is volatile, any change on the reference variable is guaranteed to be reflected.

This has nothing to do with volatile semantics. The reference to the array is created when the COWIterator constructor is called (when the iterator is created). Creating a new copy of the array and assigning its reference to the array field will keep the snapshot of the iterator pointing to the same old reference.