# Java 반복자 자바에서 컬렉션 프레임워크를 사용할 때, 우리는 여러 데이터를 저장하고 관리하는 데 있어서 다양한 자료구조를 사용한다. 이러한 자료구조들을 효율적으로 순회하고, 각 요소에 접근하며, 필요한 경우 요소를 수정하거나 삭제하는 등의 작업을 수행하기 위해 자바는 `Iterator` 인터페이스를 제공한다. ## Iterator란? ![image](https://hackmd.io/_uploads/H14Wkjw1R.png) `Iterator`는 자바의 컬렉션 프레임워크에서 컬렉션 내의 각 요소에 접근하는 수단을 제공하는 인터페이스이다. 컬렉션 프레임워크에 속하는 클래스들은 `Iterable` 인터페이스를 구현하고 있으며, 이를 통해 `Iterator`를 반환하는 `iterator()` 메소드를 제공한다. `Iterator`를 사용하면 컬렉션의 요소를 순차적으로 탐색할 수 있으며, 요소를 삭제하는 등의 작업도 수행할 수 있다. ## Iterator의 주요 메소드 `Iterator` 인터페이스에는 다음과 같은 주요 메소드가 정의되어 있다 - `boolean hasNext()`: 탐색할 다음 요소가 있는지 확인한다. 다음 요소가 있으면 `true`를, 없으면 `false`를 반환한다. - `E next()`: 다음 요소를 반환한다. `hasNext()` 메소드를 통해 다음 요소의 존재 여부를 먼저 확인한 후 호출하는 것이 안전하다. - `void remove()`: `next()` 메소드를 통해 최근에 반환된 요소를 컬렉션에서 삭제한다. `next()` 메소드 호출 직후에만 사용할 수 있다. ## Iterator를 사용하는 이유 `Iterator`를 사용하는 이유는 컬렉션 프레임워크에 대해 공통으로 사용이 가능하고 사용법이 간단하기 때문이다. 또한, `Iterator`를 사용하면 컬렉션의 내부 구조에 상관없이 요소를 순회하고 접근할 수 있으며, 요소를 안전하게 삭제할 수 있다. `Iterator`를 사용하면 컬렉션의 요소를 순회하면서 요소를 추가하거나 삭제하는 등의 작업을 수행할 수 있다. 이는 일반적인 `for` 루프나 `for-each` 루프를 사용할 때는 다소 버그가 발생할 수 있는 상황에서 안전하게 요소를 다룰 수 있다. ## Iterator 사용 예제 다음은 `ArrayList` 컬렉션을 생성하고 이를 `Iterator`를 사용해 순회하며 요소를 출력하는 간단한 예제이다 ```java import java.util.ArrayList; import java.util.Iterator; public class IteratorExample { public static void main(String[] args) { // ArrayList 생성 ArrayList<String> cars = new ArrayList<String>(); cars.add("Volvo"); cars.add("BMW"); cars.add("Ford"); cars.add("Mazda"); // Iterator를 사용하여 ArrayList 순회 Iterator<String> it = cars.iterator(); while(it.hasNext()) { String car = it.next(); System.out.println(car); } } } ``` 위 예제에서는 `ArrayList`에 여러 자동차 브랜드 이름을 저장한 후, `ArrayList`의 `iterator()` 메소드를 호출하여 `Iterator`를 얻는다. 그 다음, `hasNext()` 메소드를 사용해 순회할 요소가 남아 있는지 확인하고, `next()` 메소드를 호출하여 각 요소를 출력한다. ## Iterator를 사용한 요소의 삭제 `Iterator`의 `remove()` 메소드를 사용하면 반복문을 실행하는 도중에도 안전하게 컬렉션의 요소를 삭제할 수 있다. 다음은 `Iterator`를 사용해 특정 조건을 만족하는 요소를 삭제하는 예제이다 ```java import java.util.ArrayList; import java.util.Iterator; public class IteratorRemoveExample { public static void main(String[] args) { ArrayList<Integer> numbers = new ArrayList<Integer>(); numbers.add(1); numbers.add(2); numbers.add(3); numbers.add(4); numbers.add(5); // Iterator를 사용하여 조건에 맞는 요소 삭제 Iterator<Integer> it = numbers.iterator(); while(it.hasNext()) { Integer number = it.next(); if(number % 2 == 0) { // 짝수인 경우 it.remove(); // 해당 요소 삭제 } } // 삭제 후 컬렉션 내용 출력 System.out.println(numbers); } } ``` 이 예제에서는 `numbers` 리스트에서 짝수인 요소를 모두 삭제한다. `Iterator`의 `remove()` 메소드는 `next()` 메소드를 통해 반환된 가장 최근의 요소를 삭제하기 때문에, `next()` 호출 후 조건을 확인하고 삭제를 수행한다. ## Iterator를 사용하여 안전하게 컬렉션 수정하기 `Iterator`를 사용하면 컬렉션을 안전하게 수정할 수 있다. `Iterator`를 사용하지 않고 컬렉션을 순회하면서 요소를 추가하거나 삭제하는 경우, `ConcurrentModificationException` 예외가 발생할 수 있다. 이는 컬렉션을 순회하면서 요소를 추가하거나 삭제하는 경우, 컬렉션의 상태가 변경되어 순회 중에 예상치 못한 결과가 발생할 수 있기 때문이다. 만약 다음과 같이 `for` 루프를 사용하여 요소를 삭제하려고 하는 경우 홀수 번째 요소만 삭제되고, 짝수 번째 요소는 삭제되지 않는다. ```java import java.util.ArrayList; public class Main { public static void main(String[] args) { ArrayList<Integer> numbers = new ArrayList<>(); numbers.add(1); numbers.add(2); numbers.add(3); numbers.add(4); numbers.add(5); for (int i = 0; i < numbers.size(); i++) { numbers.remove(i); } System.out.println(numbers); } } // 출력 결과 : [2, 4] ``` Enhanced for loop를 사용하여 컬렉션을 순회하면서 요소를 삭제하려고 하는 경우, `ConcurrentModificationException` 예외가 발생한다 ```java import java.util.ArrayList; public class Main { public static void main(String[] args) { ArrayList<Integer> numbers = new ArrayList<>(); numbers.add(1); numbers.add(2); numbers.add(3); numbers.add(4); numbers.add(5); int index = 0; for(Integer number : numbers) { numbers.remove(index++); // ConcurrentModificationException 발생 } System.out.println(numbers); } } ``` 위 코드에서 `numbers` 리스트를 순회하면서 짝수인 요소를 삭제하려고 하면, `ConcurrentModificationException` 예외가 발생한다. 이는 `for-each` 루프를 사용할 때 내부적으로 `Iterator`를 사용하기 때문에, `Iterator`를 사용하지 않고 요소를 삭제하려고 하면 예외가 발생하는 것이다. `Iterator`를 사용하면 이러한 문제를 해결할 수 있다. `Iterator`를 사용하면 컬렉션을 안전하게 순회하고, 요소를 추가하거나 삭제할 수 있다. ```java import java.util.ArrayList; import java.util.Iterator; public class Main { public static void main(String[] args) { ArrayList<Integer> numbers = new ArrayList<>(); numbers.add(1); numbers.add(2); numbers.add(3); numbers.add(4); numbers.add(5); Iterator<Integer> it = numbers.iterator(); while (it.hasNext()) { Integer number = it.next(); it.remove(); } System.out.println(numbers); } } // 출력 결과 : [] ``` 위 코드에서는 `Iterator`를 사용하여 `numbers` 리스트를 순회하면서 모든 요소를 삭제한다. `Iterator`를 사용하면 안전하게 컬렉션을 수정할 수 있으며, `ConcurrentModificationException` 예외가 발생하지 않는다. ## Iterable 인터페이스 `Iterable` 인터페이스는 자바의 컬렉션 프레임워크에서 컬렉션을 순회할 수 있는 수단을 제공하는 인터페이스이다. `Iterable` 인터페이스는 다음과 같이 정의되어 있다 ```java public interface Iterable<T> { Iterator<T> iterator(); } ``` `Iterable` 인터페이스는 `iterator()` 메소드를 정의하고 있으며, 이를 통해 `Iterator`를 반환한다. 따라서, `Iterable` 인터페이스를 구현한 클래스는 `Iterator`를 반환하는 `iterator()` 메소드를 제공해야 한다. ### Iterable 구현 예제 다음은 `Iterable` 인터페이스를 구현한 `MyArrayList` 클래스의 예제이다 ```java import java.util.Iterator; public class MyArrayList<T> implements Iterable<T> { private T[] array; private int size; public MyArrayList() { array = (T[]) new Object[10]; size = 0; } public void add(T value) { if (size >= array.length) { T[] newArray = (T[]) new Object[array.length * 2]; System.arraycopy(array, 0, newArray, 0, array.length); array = newArray; } array[size++] = value; } public T get(int index) { return array[index]; } public int size() { return size; } public void remove(int index) { System.arraycopy(array, index + 1, array, index, size - index - 1); size--; } @Override public Iterator<T> iterator() { return new Iterator<T>() { private int index = 0; @Override public boolean hasNext() { return index < size; } @Override public T next() { return array[index++]; } @Override public void remove() { System.arraycopy(array, index, array, index - 1, size - index); size--; index--; } }; } public static void main(String[] args) { MyArrayList<Integer> list = new MyArrayList<>(); list.add(1); list.add(2); list.add(3); list.add(4); list.add(5); for (Integer number : list) { System.out.print(number + " "); } } } ``` 위 코드에서 `MyArrayList` 클래스는 `Iterable` 인터페이스를 구현하고 있으며, `iterator()` 메소드를 구현하여 `Iterator`를 반환한다. 따라서, `MyArrayList` 클래스는 `for-each` 루프를 사용하여 순회할 수 있다. ### Iterable 인터페이스를 사용하는 이유 `Iterable` 인터페이스를 사용하는 이유는 다음과 같다 1. **향상된 for 루프 지원**: `Iterable` 인터페이스를 구현한 클래스는 향상된 for 루프를 사용하여 컬렉션을 순회할 수 있다. 이는 코드의 가독성을 높이고, 반복 작업을 간소화한다. 2. **다양한 데이터 구조에 대한 일관된 접근 방식**: `Iterable` 인터페이스를 구현한 클래스는 컬렉션 프레임워크의 일부로 간주되어, 다양한 데이터 구조에 대해 일관된 접근 방식을 제공한다. 3. **컬렉션과 비컬렉션 데이터 소스의 일반화**: `Iterable` 인터페이스를 구현한 클래스는 컬렉션 뿐만 아니라, 배열, 파일, 네트워크 등 다양한 데이터 소스에 대해 일반화된 접근 방식을 제공한다. 4. **라이브러리와 프레임워크와의 호환성**: `Iterable` 인터페이스를 구현한 클래스는 자바의 다양한 라이브러리와 프레임워크와 호환성을 제공한다. 예를 들어, 스트림 API, 컬렉션 프레임워크 등과의 연동이 용이하다. 5. **확장성과 유연성**: `Iterable` 인터페이스를 구현한 클래스는 컬렉션 프레임워크의 다양한 기능을 활용할 수 있으며, 필요한 경우 커스텀한 기능을 추가할 수 있다. 6. **스트림 API와의 연동**: `Iterable` 인터페이스를 구현한 클래스는 스트림 API와의 연동이 용이하다. 스트림 API는 컬렉션을 다루는 데 유용한 기능을 제공하며, `Iterable` 인터페이스를 구현한 클래스는 스트림 API와 함께 사용할 수 있다.