Что использование кода в реализации AbstractCollection toArray Method

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;
}

здесь код реализации метода AbstractCollection.toArray.

if (! it.hasNext()) // fewer elements than expected
    return Arrays.copyOf(r, i);

Я не понимаю использование вышеприведенного кода. Я подозреваю, что код используется, чтобы избежать изменения размера во время вызова метода. Поэтому у меня есть два вопроса:

  • Что я подозреваю, правильно или неправильно? если это неправильно, что такое использование этого кода?
  • Если это правда, какая ситуация может изменить размер при вызове метода?

Ответ 1

Ну, метод javadoc sais все:

 /**
     * {@inheritDoc}
     *
     * <p>This implementation returns an array containing all the elements
     * returned by this collection 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>
     */

Я нахожу здесь две интересные вещи:

  • Да, вы правы, так как javadoc sais, этот метод готов к возврату корректно, даже если коллекция была изменена в среднем. Вот почему первоначальный размер - всего лишь намек. Использование итератора также обеспечивает исключение из исключения "параллельной модификации".

  • Очень легко представить многопоточную ситуацию, когда один поток добавляет/удаляет элементы из коллекции, в то время как другой поток вызывает метод toArray на нем. В такой ситуации, если сборка не является потокобезопасной (например, полученная с помощью метода Collections.synchronizedCollection(...) или вручную создавая синхронизированный код доступа к ней), вы попадаете в ситуацию, когда она модифицируется и одновременно изменяется.

Ответ 2

Я просто хочу упомянуть, что в соответствии с javadoc метод size() может возвращать максимум Integer.MAX_VALUE. Но если ваша коллекция содержит больше элементов, вы не можете получить нужный размер.

Ответ 3

вы правы, массив инициализируется с помощью size(), поэтому, если какой-либо элемент удаляется во время заполнения массива, вы можете воспользоваться этой проверкой.

Коллекции по умолчанию не являются потокобезопасными, поэтому другой поток может вызвать remove(), пока выполняется итерация: -)

Ответ 4

В то время как в целом он гарантирован (например, для всех классов java.util. *), что коллекция не будет изменяться во время итерации (в противном случае возникает исключение ConcurrentModificationException), это не гарантируется для всех коллекций. Поэтому для другого потока можно добавить или удалить элементы, в то время как один поток вызывает toArray(), тем самым изменяя размер коллекции и, следовательно, результирующий массив. В качестве альтернативы, некоторые реализации могут возвращать приблизительный размер.

Поэтому, чтобы ответить на вопрос:

  • Эти две строки проверяют, был ли достигнут конец коллекции до ожидаемого размера (результат вызова size(), который определяет r.length). Если это так, будет сделана копия массива r с соответствующим размером. Помните, что невозможно изменить размер массива.

  • Как сказано, разные возможности, так как контракт на коллекцию довольно свободен. Многопоточность, приблизительные результаты по размеру() и другие.

Ответ 5

Андрей - главный ответ. corsair дает отличную оценку Integer.MAX_VALUE.

Для полноты я добавлю, что метод toArray должен работать в любой коллекции, включая:

  • массивы с использованием метода багги;
  • динамические массивы - содержимое коллекции может меняться в зависимости от других потоков (concurrency), времени или случайных чисел. Пример в псевдокоде

    Продукты питания > thingsACatholicCanEat;//если в пятницу не следует включать мясо