Рубрики
Без рубрики

Эффективная Java: предпочитайте циклы for-each традиционным циклам for

Погружение в главу 58 “Эффективная Java”. Помеченный java, эффективный для каждой архитектуры.

Основной частью многих программ является перебор групп элементов. Java предоставляет нам множество механизмов для выполнения этой итерации. У нас есть такие опции, как while циклы, for циклы, Stream s, for-each циклы и т.д. Разные механизмы лучше подходят для разных применений. Эта конкретная глава побуждает нас предпочесть циклы for-each более традиционным циклам. Прежде чем мы перейдем к тому, что могут предоставить нам циклы for-each , давайте рассмотрим некоторые примеры альтернатив.

Одним из примеров зацикливания на Collection может быть:

for(Iterator iterator = collection.getIteator(); iterator.hasNext();) {
  Element element = iterator.next();
  // Do something with element
}

или с массивом

for(int i=0; i

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

Давайте рассмотрим пример с ошибкой и посмотрим, сможете ли вы ее обнаружить:

enum Suit {CLUB, DIAMOND, HEART, SPADE }
enum Rank { ACE, DEUCE, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING }

static Collection suits = Arrays.asList(Suit.values());
static Collection ranks = Arrays.asList(Rank.values());

List deck = new ArrayList<>();
for (Iterator i = suits.iterator(); i.hasNext(); )
  for (Iterator j = ranks.iterator(); j.hasNext(); )
    deck.add(new Card(i.next(), j.next());

Ошибка, описанная выше, оказывается довольно тонкой. Проблема в том, что мы должны вызывать next() on i вне внутреннего цикла, поскольку в настоящее время мы слишком быстро перебираем эту коллекцию и пропускаем элементы. Это, скорее всего, приведет к NoSuchElementException но в довольно неудачных случаях, когда внешняя коллекция кратна внутренней, мы не получим ошибок, но код не будет делать то, что мы хотим.

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

enum Suit {CLUB, DIAMOND, HEART, SPADE }
enum Rank { ACE, DEUCE, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING }

static Collection suits = Arrays.asList(Suit.values());
static Collection ranks = Arrays.asList(Rank.values());

List deck = new ArrayList<>();
for (Suit suit : suits)
  for (Rank rank : ranks )
    deck.add(new Card(rank, suit);

Вышеприведенное в конечном итоге оказывается намного чище, а также гораздо менее подвержено ошибкам.

Учитывая все это, давайте рассмотрим случаи, когда мы не можем использовать циклы for-each:

  • Деструктивная фильтрация – Если вы будете изменять коллекцию по мере ее прохождения, вам нужно будет использовать необработанный итератор.
  • Трансформирующий Если вам нужно заменить некоторые или все значения, вы также можете не использовать цикл для каждого.
  • Параллельная итерация Если нам нужно одновременно просматривать несколько коллекций, нам потребуется больше контроля, чем может дать цикл foreach.

Хотя существует ряд ситуаций, в которых мы не можем использовать циклы for-each, обычно это исключение, и наш “обычный цикл” – отличная возможность для использования цикла for-each. Из-за преимуществ, удобства, удобочитаемости, безопасности и гибкости рекомендуется использовать циклы для каждого, когда это возможно.

Оригинал: “https://dev.to/kylec32/effective-java-prefer-for-each-loops-to-traditional-for-loops-2f2g”