As you see,the implementation is not so easy to understand, my question is :
- What will I get when the collection's elements change (size not changed) during iteration? I guess the iterator may be some kind of snapshot.
- What will I get when the collection's size is changed? I wonder if it can work correctly.
The implementation is the way it is because it's intended to handle the case where the iterator returns a different number of elements than size()
. This can occur if the collection's size changes during the iteration. The destination array is allocated based on size()
, and in the optimistic case where the size doesn't change, it's pretty straightforward. The complexity of the code comes in where the actual number of elements returned by the iterator differs from the initial value returned by size()
. If the actual number of elements is smaller, the elements are copied into a smaller array of the right size. If the actual number is bigger, the elements are copied into a larger array, and then more elements are iterated. The array is repeatedly reallocated larger if it fills up, until the iteration completes.
To your first question, the iterator doesn't necessarily take a snapshot of the elements. It depends on the actual collection implementation. Some collections (such as CopyOnWriteArrayList
) do have snapshot semantics, so if the collection is modified, the modification won't be visible to the iterator. In this case the number of elements reported by the iterator will match size()
, so no array reallocation is necessary.
Other collection implementations have different policies for what happens if the collection is modified during iteration. Some are fail-fast which means they'll throw ConcurrentModificationException
. Others are weakly consistent which means that modifications might or might not be visible to the iterator.
This applies to your second question. If the collections size changes during iteration, and if that collection's iterator supports this (i.e., it's not fail-fast), the code here will handle a different number of elements coming out of the iterator than was initially reported by size()
.
An example where this can occur is with ConcurrentSkipListSet
. This class's iterator is weakly consistent, and it inherits the toArray()
method from AbstractCollection
. Thus, while toArray()
is iterating the set in order to gather the elements into the destination array, it's entirely legal for another thread to modify the set, possibly changing its size. This can clearly cause the iterator to report a different number of elements from the initial value returned by size()
, which will cause the array reallocation code in toArray()
to be executed.