요약
•
순회하면서 값을 수정하거나 삭제하는 경우와 여러 컬렉션을 병렬적으로 순회하는 경우가 아니면 for 문보다는 for-each 문을 사용하자.
•
자주 순회할 것 같은 클래스라면 Iterable 인터페이스의 구현을 고려해보자.
for-each 문의 장점
for (int i = 0; i < a.length; i++) {
... // a[i]로 무언가 한다.
}
Java
복사
•
이러한 전통적인 for 문으로 배열을 순회하는 코드는 while 문보다는 낫지만, 방복자와 인덱스 변수 모두 코드를 지저분하게 하고 우리가 진정으로 필요하지 않다. 또한 이처럼 쓰이는 요소 종류가 늘면 오류가 생길 가능성이 높아진다.
•
이 문제들은 for-each 문을 사용하면 모두 해결된다. 방복자와 인덱스 변수를 사용하지 않으니 코드가 깔끔해지고 오류가 발생할 일도 없다.
for (Element e : elements) {
... // e로 무언가 한다.
}
Java
복사
•
위 for-each 문은 element 안의 각 원소를 e로 순회하며 처리하고, 반복 대상이 컬렉션이든 배열이든 for-each 문에서는 사람이 손으로 최적화한 것과 속도가 동일하다.
enum Suit { CLUB, DIANAMOD, 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(newCard(i.next(), j.next()));
Java
복사
•
위 코드와 같이 for 문이 중첩되면 실수가 발생할 수 있다. 위 코드에서는 바깥 for 문의 문양이 카드 한 세트가 지나고 값이 변해야 하는데 매 카드마다 변하여, 모양이 바닥나면 NoSuchElementException 예외가 발생한다. 이를 해결하기 위해서는 바깥 for 문에 값을 저장하는 변수를 따로 두어야 한다.
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()));
}
Java
복사
•
하지만 for-each 문을 사용하면 이런 문제들을 고려할 필요도 없이 아주 간단하고 깔끔하게 작성할 수 있다.
for (Suit suit : suits)
for (Rank rank : ranks)
deck.add(new Card(suit, rank));
Java
복사
for-each 문을 사용할 수 없는 상황
•
파괴적인 필터링(destructive filtering)
◦
컬렉션을 순회하면서 선택된 원소를 제거해야 한다면 반복자의 remove 메서드를 호출해야 하기 때문에, 반복자를 통한 for 문을 사용해야 한다.
•
변형(transforming)
◦
리스트나 배열을 순회하면서 그 원소의 값 일부 혹은 전체를 교체해야 한다면, 리스트의 반복자나 배열의 인덱스를 사용해야 한다.
•
병렬 반복(parallel iteration)
◦
여러 컬렉션을 병렬로 순회해야 한다면, 각각의 반복자와 인덱스 변수를 사용해 엄격하고 명시적으로 제어해야 한다.
Iterable 인터페이스
•
for-each 문은 컬렉션과 배열뿐 아니라 Iterable 인터페이스를 구현한 객체라면 아무거나 순회할 수 있다.
public interface Iterable<E> {
Iterator<E> iterator();
}
Java
복사
•
Iterable은 이와 같이 메서드가 하나뿐인데, Iterable을 처음부터 구현하는 것은 까다롭지만 원소들의 묶음을 표현하는 타입을 작성해야 한다면 구현해보는 것이 좋다.
•
Iterable을 구현해두기만 하면 for-each 문을 사용하여 간편하게 순회할 수 있다.