public Object[] toArray() {
// Estimate size of array; be prepared to see more or fewer elements
Object[] r = new Object[size()];
Iterator<E> it = iterator();
for (int i = 0; i < r.length; i++) {
if (! it.hasNext()) // fewer elements than expected
return Arrays.copyOf(r, i);
r[i] = it.next();
}
return it.hasNext() ? finishToArray(r, it) : r;
}
here's the code of implementation of AbstractCollection.toArray
method.
if (! it.hasNext()) // fewer elements than expected
return Arrays.copyOf(r, i);
I don't understand the usage of the code above. I suspect the code is used to avoid the size changing while the the method is invoked. So I have two questions:
- What I suspect is right or wrong? if it's wrong, what's the usage of this code?
- If it's true, what situation can make the size changing while the method has been invoked?
Well, the method's javadoc sais it all:
/**
* {@inheritDoc}
*
* <p>This implementation returns an array containing all the elements
* returned by this collection's iterator, in the same order, stored in
* consecutive elements of the array, starting with index {@code 0}.
* The length of the returned array is equal to the number of elements
* returned by the iterator, even if the size of this collection changes
* during iteration, as might happen if the collection permits
* concurrent modification during iteration. The {@code size} method is
* called only as an optimization hint; the correct result is returned
* even if the iterator returns a different number of elements.
*
* <p>This method is equivalent to:
*
* <pre> {@code
* List<E> list = new ArrayList<E>(size());
* for (E e : this)
* list.add(e);
* return list.toArray();
* }</pre>
*/
I find two interesting things to mention here:
Yes, you're right, as the javadoc sais, this method is prepared to return correctlly even if the Collection has been modified in the mean time. That's why the initial size is just a hint. The usage of the iterator also ensures avoidance from the "concurrent modification" exception.
It's very easy to imagine a multi-threaded situation where one thread adds/removes elements from a Collection while a different thread calls the "toArray" method on it. In such a situation, if the Collection is not thread safe (like obtained via Collections.synchronizedCollection(...)
method, or by manually creating synchronized access code towards it) you'll get into a situation where it's modified and toArray-ed at the same time.
I just want to mention that according to the javadoc, the method size()
can return maximum Integer.MAX_VALUE. But if your collection has more elements you can't get a proper size.
you are right, the array is initialized with size() so if any element is removed while the array is being populated, you would benefit from this check.
Collections are by default not thread safe, so another thread could call remove() while the iteration is in progress :-)
While it's generally guaranteed (e.g. for all java.util.* collection classes) that a collection won't change while it is iterated (otherwise throws a ConcurrentModificationException) this is not guaranteed for all collections. It's therefore possible for another thread to add or remove elements while one thread is calling toArray(), thus changing the size of collection and therefore the resulting array. Alternatively, some implementation might only return an approximate size.
Therefore, to answer the question:
These two lines check if the end of the collection was reached before the expected size (result of size() call which defines r.length) was reached. If this is the case, a copy of the array r with the appropriate size will be made. Remember that it's not possible to resize an array.
As said, different possibilities since the contract for Collection is pretty loose. Multi-threading, approximate results by size() and others.
Andrei is the main answer. corsair raises an excellent point about Integer.MAX_VALUE.
For completeness, I will add the toArray method is supposed to work on any Collection including:
- arrays with a buggy size method;
dynamic arrays - the contents of the Collection could change depending on other threads (concurrency), the time, or random numbers. An example in pseudocode
Collection < Food > thingsACatholicCanEat ; // if it is Friday should not include meat