# Java 반복자
자바에서 컬렉션 프레임워크를 사용할 때, 우리는 여러 데이터를 저장하고 관리하는 데 있어서 다양한 자료구조를 사용한다. 이러한 자료구조들을 효율적으로 순회하고, 각 요소에 접근하며, 필요한 경우 요소를 수정하거나 삭제하는 등의 작업을 수행하기 위해 자바는 `Iterator` 인터페이스를 제공한다.
## Iterator란?

`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와 함께 사용할 수 있다.