Skip to content

Latest commit

 

History

History
99 lines (70 loc) · 4.12 KB

File metadata and controls

99 lines (70 loc) · 4.12 KB

아이템 58 전통적인 for 문 보다는 for-each 문을 사용하라

반복문은 스트림이 제격인 작업이 있고 반복이 제격인 작업이 있다

// 컬렉션을 순회
for (Iterator<Element> iterator = c.iterator(); i.hasNext(); ) {
    Element e = i.next();
}
// 배열 순회하기
for (int i = 0; i < a.length; i++) {

}

이것들은 item 57 지역변수 범위를 최소화하라 에서 말했듯이 while보단 낫지만 가장 좋진않다 반복자와 인덱스 변수는 코드를 지저분하게 한다 또한 잘못된 변수를 사용하더라도 컴파일러가 완벽하게 잡아줄리라는 보장도 없다

이건 for-each문을 사용하면 모두 해결된다 반복자와 인덱스변수를 사용하지 않으니 코드가 깔끔해지고 오류가 날 일도 없다

for (Element e : elements) {

}

여기서 클론(:) 은 안의(in)이라고 읽으면 된다 따라서 이 반복문은 elements안에 각원소 e에 대해 라고 이해하면 된다그리고 반복 대상이 배열이든 컬렉션이든 for-each를사용하면 속도도 똑같다

enum Suit {HEART, DIAMOND, CLUB, SPADE}
enum Rank {ACE, ONE, TWO, 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()));
    }
}

이 반복문의 버그는 바깥 컬렉션(suits)의 반복자에서 next 메서드가 너무 많이 불린다는 점이다. 그래서 suit하나당 한번씩 모든 카드를 호출해야 하는데 안쪽 반복문에서 호출되는 바람에 rank하나당 한번씩 불리고 있다 결론은 deck.add(new Card(i.next(), j.next())); 여기서 i.next를 여기다 하면 안되고 가장 바깥쪽 for문에 해야 한다 찾는데 10분 걸렸다

위에 예제는 배열이 돌면 NoSuchElementException이 터진다 왜와이? suits의 원소가 바닥나니까

더 심각한건 예외를 터트리지 않는 경우이다

enum Face {ONE, TWO, THREE, FOUR, FIVE, SIX}

Collection<Face> faces = EnumSet.allOf(Face.class);

for(Iterator<Face> i = faces.iterator(); i.hasNext(); ) {
    for (Iterator<Face> j = faces.iterator(); j.hasNext(); ) {
        System.out.println(i.next() + ", " + j.next());
    }
}

위 예제와 에러가 나는 이유는 같지만 이렇게 배열이 바닥나지 않는 경우에는 예외를 터트리지 않고 의도와는 틀리게 6x6개의 모든 조합이 나오지 않는다 6개만 나온다

for-each로 바꿔보면

for (Suit suit : suits) {
    for (Suit rank : ranks) {
        System.out.println(suit + ", " + rank);
    }
}

너무 간결하고 완벽하게 해결된다

하지만 for-each문을 사용할 수 없는 경우도 존재한다

  1. 파괴적인 필터링

    1. 컬렉션을 순회하며 선택된 원소를 제거해야 한다면 remove메서드를 호출해야 한다
    for (Face face : faces) {
    		if(face.getValue() == "ONE") {
    				face.remove()
    		}
    }

    이러면 ConcurrentModificationException을 터트린다 → 자바에서 컬렉션을 반복하는 동안 동일한 컬렉션에 구조적인 변경이 발생했을 때 발생하는 런타임 예외

  2. 순회하면서 그 원소의 값 일부 혹은 전체를 교체해야 한다면 리스트의 반복잡나 배열의 인덱스를 사용해야한다

    1. 결국엔 반복하면서 i가 있어야 해당 인덱스를 변경하거나 교체할 수 있으니까
  3. 병렬 반복

    1. 여러 컬렉션을 병렬로 순회해야 한다면 각각의 반복자와 인덱스 변수를 사용해 엄격하고 명시적으로 제어해야 한다 왜 와이? Face face : faces 이거는 하나밖에 안되니까 여러개 쓸려면 for문써야될듯

for-each문을 사용하면 컬렉션과 배열은 물론 Iterable 인터페이스를 구현한 객체라면 무엇이든 순회할 수 있다