Spec-Zone .ru
спецификации, руководства, описания, API
Содержание документации

Цикл foreach


Итерация по набору более уродлива, чем это должно быть. Рассмотрите следующий метод, который берет набор задач таймера и отменяет их:
void cancelAll(Collection<TimerTask> c) {
    for (Iterator<TimerTask> i = c.iterator(); i.hasNext(); )
        i.next().cancel();
}

iterator является только помехой. Кроме того это - возможность для ошибки. iterator переменная происходит три раза в каждом цикле: это - две возможности понять это превратно. Для - каждая конструкция избавляется от помехи и возможности для ошибки. Вот то, как пример смотрит с для - каждая конструкция:

void cancelAll(Collection<TimerTask> c) {
    for (TimerTask t : c)
        t.cancel();
}

Когда Вы видите двоеточие (:) считайте это как "в". Цикл выше чтений как "для каждого TimerTask t в c." Как можно видеть, для - каждая конструкция объединяется красиво с обобщениями. Это сохраняет всю безопасность типов, удаляя остающуюся помеху. Поскольку Вы не должны объявить iterator, Вы не должны обеспечить универсальное объявление для него. (Компилятор делает это для Вас за Вашей спиной, но Вы не должны интересоваться этим.)

Вот частая ошибка, которую делают люди, когда они пытаются сделать вложенную итерацию более чем два набора:

List suits = ...;
List ranks = ...;
List sortedDeck = new ArrayList();

// BROKEN - throws NoSuchElementException!
for (Iterator i = suits.iterator(); i.hasNext(); )
    for (Iterator j = ranks.iterator(); j.hasNext(); )
        sortedDeck.add(new Card(i.next(), j.next()));

Можно определить ошибку? Не плохо себя чувствуйте, если Вы не можете. Много опытных программистов сделали эту ошибку в какой-то момент. Проблема состоит в том что next метод вызывают слишком много раз на "внешнем" наборе (suits). Это вызывают во внутреннем цикле и для внешних и для внутренних наборов, который является неправильным. Чтобы фиксировать это, необходимо добавить переменную в пределах внешнего цикла, чтобы содержать иск:

// Fixed, though a bit ugly
for (Iterator i = suits.iterator(); i.hasNext(); ) {
    Suit suit = (Suit) i.next();
    for (Iterator j = ranks.iterator(); j.hasNext(); )
        sortedDeck.add(new Card(suit, j.next()));
}

Так, к чему все это имеет отношение для - каждая конструкция? Это нестандартно для вложенной итерации! Пируйте глаза:

for (Suit suit : suits)
    for (Rank rank : ranks)
        sortedDeck.add(new Card(suit, rank));

Для - каждая конструкция также применима к массивам, где она скрывает индексную переменную, а не iterator. Следующий метод возвращает сумму значений в int массив:

>// Returns the sum of the elements of a>
int sum(int[] a) {
    int result = 0;
    for (int i : a)
        result += i;
    return result;
}

Так, когда следует использовать цикл foreach? Любое время Вы можете. Это действительно украшает Ваш код. К сожалению, невозможно использовать это всюду. Рассмотрите, например, expurgate метод. Программа нуждается в доступе к iterator, чтобы удалить текущий элемент. Цикл foreach скрывает iterator, таким образом, невозможно вызвать remove. Поэтому, цикл foreach не применим для того, чтобы фильтровать. Так же это не применимо для циклов, где Вы должны заменить элементы в списке или массиве, поскольку Вы пересекаете это. Наконец, это не применимо для циклов, которые должны выполнить итерации по многократным наборам параллельно. Эти недостатки были известны разработчикам, которые приняли сознательное решение пойти с чистой, простой конструкцией, которая покроет значительное большинство случаев.


Oracle и/или его филиалы Авторское право © 1993, 2011, Oracle и/или его филиалы. Все права защищены.
Свяжитесь с Нами