도당탕탕

Item58 : 전통적인 FOR LOOP 보다 FOR EACH LOOP를 선호하라. 본문

JAVA

Item58 : 전통적인 FOR LOOP 보다 FOR EACH LOOP를 선호하라.

backlo 2023. 1. 12. 11:18
for (Element e : elements) {
  ...// Do something with e
}
  • 위는 for each element e in elements 라고 읽힌다.
  • foreach 문은 전통적인 for loop와 비교했을 때 성능차이가 없다.

FOR EACH의 장점

// Can you spot the bug?
enum Suit { CLUB, DIAMOND, HEART, SPADE }
enum Rank { ACE, DEUCE, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT,
NINE, TEN, JACK, QUEEN, KING } ...

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

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

위 코드에는 버그가 있다. 바로 바깥 for문에 있는 Iterator i가 너무 많이 호출된다는 점이다. 결국 NoSuchElementException 이 발생할 가능성이 있고, exception이 나지 않더라도 비정상적인 결과가 나타나게 된다.

다음처럼 Suit 값을 저장해서 해결할 수 있다.

// Fixed, but ugly - you can do better!
for (Iterator<Suit> i = suits.iterator(); i.hasNext(); ) { 
  Suit suit = i.next();
    for (Iterator<Rank> j = ranks.iterator(); j.hasNext(); ) 
    deck.add(new Card(suit, j.next()));
}

하지만 중첩 for-each문을 사용해서 더 간결하게 문제를 해결할 수 있다.

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

for-each를 쓸 수 없는 경우

  • 파괴적 필터링 - Collection을 순회하면서 remove 함수를 호출해야 하는 경우
  • 변환 - 리스트 또는 배열을 순회하면서 일부 값들을 치환해야 하는 경우, 인덱스나 iterator를 사용할 수밖에 없다.
  • 병렬 순회 - 여러 컬레션들을 병렬로 순회해야 할 경우

Iterable interface를 구현하는 객체들은 모두 for-each 문으로 순회가능하다.

public interface Iterable<E> {
// Returns an iterator over the elements in this iterable Iterator<E> iterator();
}

정리

for-each loop는 명료성, 유연함, 버그 방지, no performance penalty(성능저하 없음) 측면에서 확실한 장점이 있으므로, 가능하면 for loop 대신에 for-each 문을 사용하라.

Comments