전통적인 for 보다는 for-each를 사용하자
일반적인 for 문의 단점
- 반복자(iterator)나 index와 같은 변수들을 사용하게 된다.
- 코드가 지저분해 보임
- 어쨋거나 변수가 존재하기 때문에 “변수를 잘못 사용하는 경우", 오류 발생 확률이 그만큼 높아진다.
- 컨테이너에 따라 코드가 달라진다.(List였다면 get, 배열이였다면 [idx])
- 아래의 예시는 iterator라는 변수를 사용하여 접근하고 있음
- iterator는 해당 컨테이너 사이즈 만큼의 원소에만 접근이 가능한데, 아래 예시는 해당 사이즈를 넘어서고 있기 때문에 NoSuchElementException이 발생
enum OUTER {A,B}; enum INNER {ONE,TWO,THREE} static List<OUTER>outers= Arrays.asList(OUTER.values()); static List<INNER>inners= Arrays.asList(INNER.values()); private class Pair { private OUTER outer; private INNER inner; public Pair(OUTER outer, INNER inner) { this.outer = outer; this.inner = inner; } } private void exceedSize() { List<Pair> pairs = new ArrayList<>(); for(Iterator<OUTER> outer =outers.iterator(); outer.hasNext();) { for(Iterator<INNER> inner =inners.iterator(); inner.hasNext();) { pairs.add(new Pair(outer.next(), inner.next())); } } } main.exceedSize(); // NoSuchElementException💥
- 위의 문제를 해결하기 위해서는 아래처럼 해결할 수 있긴 하다.
private void exceedSizeV2() { List<Pair> pairs = new ArrayList<>(); for(Iterator<OUTER> outer =outers.iterator(); outer.hasNext();) { OUTER curOuter = outer.next(); // outers 의 size 만큼만 받아 쓸 수 있도록 for(Iterator<INNER> inner =inners.iterator(); inner.hasNext();) { pairs.add(new Pair(curOuter, inner.next())); } } }
Advanced for 문을 사용하자
- 아래의 코드는 훨씬 깔끔 명료하다.
- 위에서 변수를 사용하며 나타나던 문제들이 발생하지 않는다는 장점도 있다.
List<Pair> pairs = new ArrayList<>(); for (OUTER outer :outers) { for (INNER inner :inners) { pairs.add(new Pair(outer, inner)); } }
for-each의 동작원리?
- Java5에 도입된 for each문은 내부적으로는 iterator를 사용하여 동작한다.
- 다만 이 변수를 사용자에게 노출하지 않는 것 뿐이다.
- 따라서 for each를 사용한다고 성능저하가 있지는 않다.
[하지만 원시타입의 경우는 속도 차이가 조금 있다]
- for each는 내부적으로 iterator를 사용하고 있다 iterator는 Generic이기 때문에 원시타입의 경우 Boxing, Unboxing이 일어나기 때문에 index를 사용할 때 보다 성능 저하가 존재한다
@Nested @DisplayName("원시타입에 대해 index 접근 및 for each 접근 속도 테스트") class PrimitveForTest { private int[] ints; private int sum; @BeforeEach void setUp() { ints = IntStream.range(0,100000000).toArray(); sum = 0; } @Test @DisplayName("index 로 전체 원소들을 Iterate 하는데 걸리는 시간") void iterateV2() { for (int idx = 0; idx < ints.length; idx++) { sum += ints[idx]; } } @Test @DisplayName("for each 로 전체 원소들을 iterate 하는데 걸리는 시간") void iterateV1() { for (int numb : ints) { sum += numb; } } }

Iterable 인터페이스
- 이 for-each 문은, Iterable 인터페이스를 구현한 객체라면 무엇이든 순회할 수 있다.
- 컬렉션이 아니더라도, 원소의 묶음을 표현하는 타입을 작성할 때면 Iterable 을 구현할 경우, 해당 타입 사용자들에게 많은 편의성을 제공해 줄 수 있다.
그럼에도 일반적인 for 문을 사용해야 하는 경우
- 컨테이너 내부 원소들에 대한 변경이 필요한 경우에는 일반적인 For문을 사용해야 한다.
- 이 경우에는 index, Iterator를 사용해야 한다.
결론
- 특별히 인덱스가 필요하지 않은 경우라면 for-each를 사용하여 인덱스를 직접 사용하게 함으로써 발생할 수 있는 오류의 상황을 줄이도록 하자