2) Более того, почему мы используем цикл for в этом условном выражении вместо - while (hasNext())
Я предполагаю, что этот вопрос относится к последнему циклу for в указанном коде, который проходит по итераторам:
ListIterator<? super T> di = dest.listIterator();
ListIterator<? extends T> si = src.listIterator();
for (int i = 0; i < srcSize; ++i) {
di.next();
di.set(si.next());
}
Большинство обычных циклов итераторов выглядят примерно так:
Iterator<T> it = src.iterator();
while (it.hasNext()) {
T t = it.next();
// process t
}
Этот код этого не делает. На самом деле, он не вызывает hasNext()
на обоих итераторах, что довольно необычно. (Итераторы являются экземплярами ListIterator
вместо Iterator
из-за необходимости вызова метода set()
. Использование ListIterator
не имеет отношения к вопросу управления циклом, который также применим к Iterator
. )
Причина, по которой hasNext()
не вызывается в цикле, состоит в том, что код уже знает размеры коллекции. Он знает количество копируемых элементов, которое составляет srcSize
. Он уже проверил размер назначения, чтобы убедиться, что он достаточно большой, чтобы получить все элементы. Таким образом, цикл может вызывать si.next()
и di.next()
до srcSize
раз без необходимости вызова каких-либо hasNext()
методов. Единственный способ, которым вызов next()
может потерпеть неудачу, - это если один из итераторов коллекции реализован неправильно.
(Цикл также может завершиться ошибкой, если один из списков одновременно изменяет размер. Хммм. Этот код явно предполагает, что размер ни одного из списков не изменяется во время итерации.)
При условии, что размеры списков не меняются, нет причин для вызова любых методов hasNext()
. Поскольку число копируемых элементов известно, можно использовать подсчитанный цикл, и он более эффективен, поскольку он избегает любых вызовов методов как части логики управления циклом. То есть i < srcSize
и ++i
компилируются в линию по сравнению с чем-то вроде while (si.hasNext())
, что, конечно, требует вызова метода.