본문 바로가기

Study/Java

Iterator 와 For-each 비교

반응형

Iterator

이터레이터(Iterator)는 자바 컬렉션 프레임워크(Collection Framework)에 속해있는 인터페이스 중 하나.

Iterato를 구현한 클래스는 컬렉션 객체를 탐색, 데이터에 접근, 삭제할 수 있다.

 

Iterator 예제

public class Tester {

   public static void main(String[] args) {
      List<Integer> list = new ArrayList<>();
      list.add(1);
      list.add(2);
      list.add(3);
      list.add(4);
      list.add(5);
      list.add(6);

      System.out.println("List: ");

      Iterator<Integer> listIterator = list.iterator(); // Iterator 타입 변수 생성 및 초기화
      while(listIterator.hasNext()){
         System.out.print(listIterator.next() + " ");
      }
   }
}

 


For-each(강화된 for 반복문)

'강화된 for 반복문(Enhanced for loop)'라고도 불리는 For-each 반복문은 자바 1.8 버전에서 추가됐으며 for/while,do-while 반복문과 비슷하게 동작하는 반복문이다.

 

다른 반복문과 다른점은 For-each 반복문은 이터레이터의 단점을 극복하기 위해 나왔다.

 

이터레이터를 사용하려면 매번 Iterator 타입의 변수 생성, 초기화해야 한다. 그리고 반복문을 통해 이터레이터의 내부에 있는 커서를 옮겨서 컬렉션 아이템을 탐색할 수 있다.

 

Iterator타입 객체를 사용하여 아이템을 탐색할 경우 기본 코드(boilerplate code)가 많아 진다.

예를 들어, 2차원 탐색처럼 컬렉션 아이템이 또다른 컬렉션 아이템을 담고 있는 경우 Iterator 타입의 변수 두 개를 생성하고, 초기화하고, 반복문을 코드를 작성해야 한다.

 

For-each 예제

public class Tester {

   public static void main(String[] args) {
      List<Integer> list = new ArrayList<>();
      list.add(1);
      list.add(2);
      list.add(3);
      list.add(4);
      list.add(5);
      list.add(6);

      System.out.println("List: ");
      for (int i : list) {
         System.out.print(i + " ");
      }
   }
}

 


Iterator와 For-each의 차이점

차이점 1. For-each 반복문은 처음부터 끝까지 모두 탐색한다.

공식 문서 정의에서 컬렉션 아이템을 처음부터 끝까지 모두 탐색하거나, 탐색 도중 예외(Exception)가 발생할 때까지 정해진 동작(탐색)을 수행한다고 되어 있다.

 

이전 For-each 예제처럼 1부터 6까지 정수값을 담고 있는 ArrayList 타입 컬렉션 객체를 For-each 반복문으로 탐색할 경우 1부터 3까지만 탐색하는것은 기본 동작에 없다는 것이다. if문 같은 조건문을 사용해서 예외처리를 해야만 반복문을 빠져 나올 수 있다.

 

차이점 2. For-each문은 ConcurrentModificationException 예외가 발생할 수 있다.

아래 코드는 물류창고에서 보관 중인 물품이 상했는지 확인하고(isContaminated() 메서드), 상했다면 해당 물품을 폐기하는(remove() 메서드) 작업을 하는 코드다.

 

class Inventory {

    private List<Supply> supplies = new ArrayList<>();

    void disposeConteminatedSupplies() {
        for (Supply supply : supplies) {
            if (supply.isContaminated()) {
                supplies.remove(supply);
            }
        }
    }
}

 

문제가 없어 보이지만 Supply 객체의 아이템 중 폐기해야 할 물품이 있어서 remove() 메서드를 실행하게 되면 ConcurrentModificationException 예외가 발생하게 된다. 이 예외는 수정할 수 없는 아이템을 수정하려는 상황에 발생한다. 

 

For-each 반복문에서 각 아이템은 Iterable 타입이다. Iterable 인터페이스에는 컬렉션 아이템을 지우는 remove() 메서드 정의가 없다. Iterator 타입 아이템은 remove() 메서드를 이용하여 해당 아이템을 삭제할 수 있지만, Iterable 아이템에서는 불가능하다. Iterator와 비슷하게 탐색할 수는 있지만 100% 동일하지는 않은 것이다.

 


 

평소에 for / For-each / switch / while 등 여러 반복문을 편한대로 사용했었는데 이런 문제가 발생할 수 있다는 것을 책 '자바 코딩의 기술'을 읽으면서 세세하게 살펴보게 됐다. 기본에 충실해야 한다는게 이런건가보다.

 


참고 도서

자바 코딩의 기술 2.4장 '순회하며 컬렉션 수정하지 않기'

자바 코딩의 기술
국내도서
저자 : 사이먼 하러,요르그 레너드,리누스 디에츠 / 심지현역
출판 : 길벗 2020.07.30
상세보기

 

참고 사이트

Iterator vs forEach in Java : Link

Iterator vs Foreach In Java : Link

JSR-201 : Link

forEach : Link

The For-Each Loop : Link

ConcurrentModificationException : Link

Iterable<T> : Link

Iterator<E> : Link

 

 
반응형