핵심 정리
가능한 모든 곳에서 for문이 아닌 for-each문을 사용하자.
for문 vs for-each문
// 컬렉션 순회하기 - 더 나은 방법이 있다.
for (Iterator<Element> i = c.iterator(); i.hasNext(); ){
Element e = i.next();
... //e로 무언가를 한다.
}
// 배열 순회하기 - 더 나은 방법이 있다.
for (int i = 0; i < a.length; i++) {
..// a[i]로 무언가를 한다.
- 위 방법은 while 문보다는 낫지만(item 57) 그렇다고 가장 좋은 방법은 아니다.
- 이유는 다음과 같다.
- 반복자와 인덱스 변수는 코드만 지저분하게 할 뿐 필요한 것은 원소들 뿐이다.
- 반복자와 인덱스 변수를 잘못 사용할 가능성이 높아진다.
- 컬렉션인지 배열인지에 따라 코드 형태가 달라진다.
- 이러한 문제들은 for-each문을 사용하면 모두 해결된다.
// 컬렉션과 배열을 순회하는 올바른 관용구
for (Element e : elements) {
..// e로 무언가를 한다.
}
for-each문(enhanced for statement)의 장점
- 성능 저하도 없다.
- 하나의 관용구로 컬렉션, 배열, Iterable 인터페이스를 구현한 객체까지 모두 처리할 수 있다.
public interface Iterable<E> {
// 이 객체의 원소들을 순회하는 반복자를 반환한다.
Iterator<E> iterator();
}
- 컬렉션이 중첩되는 경우 for-each의 이점이 커진다.
예시 - 컬렉션 중첩 순회의 경우
// for문 - 버그 발생
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문이 한 번 돌 때마다(즉, Suit 한 종류당), 안쪽의 for문은 Rank의 모든 종류를 돌아야 한다. 그러나 바깥쪽의 next()가 안쪽 for 문에서 호출되므로 Rank 하나당 Suit 하나를 가져오게 된다. 그 결과 Suit가 먼저 소진되어 NoSuchElementException이 발생한다.
// for-each문 - 버그 해결
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(Suit suit : suits) {
for(Rank rank : ranks) {
deck.add(new Card(suit, rank));
}
}
위 코드는 각각의 Suit에 대해 모든 Rank를 순회하며 Card를 생성하므로, 모든 조합의 카드를 만들 수 있게 된다.
for-each문을 사용할 수 없는 세 가지 경우
: 반복자나 인덱스가 필요하기 때문
아래 세 가지 경우를 기억할 필요는 없다.
반복자나 인덱스가 필요할 경우 for-each 문을 사용하지 못하는 것은 당연하니까 ..
파괴적인 필터링(destructive filtering)
: 컬렉션을 순회하면서 특정 조건을 충족하는 원소들을 제거하는 작업
- 파괴적인 필터링을 하려면 반복자의 remove 메소드를 호출해야 한다.
- 또는 Java8부터 제공하는 Collection.removeIf() 메소드를 사용할 수 있다.
List<Integer> numbers = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
Iterator<Integer> iterator = numbers.iterator();
while (iterator.hasNext()) {
Integer number = iterator.next();
if (number % 2 != 0) {
iterator.remove();
}
}
변형(transforming)
: 컬렉션을 순회하면서 원소의 값을 변경하는 작업
- 변형을 하려면 리스트의 반복자나 배열의 인덱스를 사용해야 한다.
List<Integer> numbers = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
for (int i = 0; i < numbers.size(); i++) {
numbers.set(i, numbers.get(i) * numbers.get(i));
}
병렬 반복(parallel iteration)
: 여러 컬렉션을 동시에 순회하는 작업
- 병렬 반복을 하려면 각 컬렉션의 반복자나 인덱스를 사용해야 한다.
List<Integer> numbers1 = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
List<Integer> numbers2 = new ArrayList<>(Arrays.asList(6, 7, 8, 9, 10));
for (int i = 0; i < numbers1.size(); i++) {
System.out.println(numbers1.get(i) + ", " + numbers2.get(i));
}
'Language > Java' 카테고리의 다른 글
| [effective java] 아이템 60. 정확한 답이 필요하다면 float와 double은 피하라. (0) | 2023.07.17 |
|---|---|
| [effective java] 아이템 59. 라이브러리를 익히고 사용하라. (0) | 2023.07.17 |
| [effective java] 아이템 57. 지역변수의 범위를 최소화하라. (0) | 2023.07.17 |
| [effective java] 아이템 56. 공개된 API 요소에는 항상 문서화 주석을 작성하라. (0) | 2023.07.17 |
| [effective java] 아이템 55. 옵셔널 반환은 신중히 하라. (0) | 2023.07.17 |