📚 오늘 공부한 내용 - Iteration (순회)
🎯 순회(Iteration)란?
순회는 자료구조에 들어있는 데이터를 차례대로 접근해서 처리하는 것을 말합니다. 여러 곳을 돌아다닌다는 의미로, 컬렉션의 모든 요소를 하나씩 방문하는 과정입니다.
🤔 문제 상황: 각기 다른 순회 방법들
각 자료구조마다 내부 구조가 다르기 때문에 순회 방법도 모두 달랐습니다:
// 배열 리스트 - 인덱스 기반
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
// 연결 리스트 - 노드 기반
Node current = first;
while (current != null) {
System.out.println(current.item);
current = current.next;
}
문제점: 자료구조가 바뀔 때마다 순회 코드를 다시 작성해야 하고, 내부 구조까지 알아야 함
💡 해결책: Iterable & Iterator 패턴
Java는 이 문제를 해결하기 위해 Iterator 패턴을 도입했습니다. 모든 자료구조를 동일한 방법으로 순회할 수 있게 하는 일관된 인터페이스를 제공합니다.
🔧 핵심 인터페이스들
1️⃣ Iterable 인터페이스
public interface Iterable<T> {
Iterator<T> iterator(); // 반복자를 반환하는 메서드
}
- 의미: "반복 가능한" 객체임을 나타냄
- 역할: Iterator를 제공하는 팩토리 메서드
2️⃣ Iterator 인터페이스
public interface Iterator<E> {
boolean hasNext(); // 다음 요소가 있는지 확인
E next(); // 다음 요소 반환하고 위치 이동
}
- 의미: "반복자" - 실제 순회를 담당
- 역할: 순회 상태를 관리하고 요소에 접근
🏗️ 직접 구현해보기
Iterator 구현체
public class MyArrayIterator implements Iterator<Integer> {
private int currentIndex = -1; // 현재 위치 (-1부터 시작)
private int[] targetArr; // 순회할 배열
public MyArrayIterator(int[] targetArr) {
this.targetArr = targetArr;
}
@Override
public boolean hasNext() {
return currentIndex < targetArr.length - 1;
}
@Override
public Integer next() {
return targetArr[++currentIndex]; // 인덱스 증가하고 반환
}
}
Iterable 구현체
public class MyArray implements Iterable<Integer> {
private int[] numbers;
public MyArray(int[] numbers) {
this.numbers = numbers;
}
@Override
public Iterator<Integer> iterator() {
return new MyArrayIterator(numbers); // Iterator 생성해서 반환
}
}
🎮 사용 방법
MyArray myArray = new MyArray(new int[]{1, 2, 3, 4});
Iterator<Integer> iterator = myArray.iterator();
// 전통적인 방법
while (iterator.hasNext()) {
Integer value = iterator.next();
System.out.println("value = " + value);
}
✨ 향상된 for문(Enhanced For Loop)
Iterable을 구현한 객체는 자동으로 **향상된 for문(for-each문)**을 사용할 수 있습니다!
// 깔끔한 for-each 문법
for (int value : myArray) {
System.out.println("value = " + value);
}
🔄 컴파일러의 마법
위의 for-each문은 컴파일 시점에 다음과 같이 자동 변환됩니다:
Iterator<Integer> iterator = myArray.iterator();
while (iterator.hasNext()) {
Integer value = iterator.next();
System.out.println("value = " + value);
}
두 코드는 완전히 동일합니다! for-each문이 더 간결하고 읽기 쉽죠.
🏛️ Java 컬렉션 프레임워크와 Iterator
📊 모든 컬렉션이 Iterable 구현
Java의 모든 컬렉션은 이미 Iterable과 Iterator를 구현해두었습니다:
Iterable
↓
Collection ──────── Map
↓ ↓
┌───────────┼───────┐ └── keySet(), values(),
List Set Queue entrySet()으로 순회 가능
↓ ↓ ↓
ArrayList HashSet ArrayDeque
LinkedList TreeSet LinkedList
🎯 다형성의 힘
private static void printAll(Iterator<Integer> iterator) {
System.out.println("iterator = " + iterator.getClass());
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
private static void foreach(Iterable<Integer> iterable) {
System.out.println("iterable = " + iterable.getClass());
for (Integer i : iterable) {
System.out.println(i);
}
}
// 사용법 - 어떤 컬렉션이든 동일하게 처리!
List<Integer> list = new ArrayList<>();
Set<Integer> set = new HashSet<>();
printAll(list.iterator()); // ArrayList의 Iterator
printAll(set.iterator()); // HashSet의 Iterator
foreach(list); // ArrayList를 for-each로
foreach(set); // HashSet을 for-each로
🎁 Iterator 패턴의 장점
1️⃣ 일관성 (Consistency)
- 모든 자료구조를 동일한 방법으로 순회
- 새로운 자료구조를 배워도 순회 방법은 동일
2️⃣ 추상화 (Abstraction)
- 자료구조의 내부 구조를 몰라도 순회 가능
- ArrayList인지 LinkedList인지 신경 쓸 필요 없음
3️⃣ 캡슐화 (Encapsulation)
- 자료구조의 내부 구현이 외부에 노출되지 않음
- 구현 변경 시에도 클라이언트 코드는 영향 없음
4️⃣ 다형성 (Polymorphism)
- Iterator/Iterable 인터페이스로 통일된 처리
- 런타임에 실제 구현체가 결정됨
5️⃣ 확장성 (Extensibility)
- 새로운 자료구조 추가 시 기존 코드 재사용 가능
- Iterator만 구현하면 모든 순회 알고리즘 호환
🎭 디자인 패턴의 관점
Iterator 패턴은 GoF 디자인 패턴 중 하나입니다:
- 목적: 컬렉션의 내부 표현을 노출하지 않고 순차적으로 접근
- 구조: Aggregate(컬렉션)와 Iterator(반복자)의 분리
- 효과: 순회 알고리즘과 자료구조의 독립성 확보
🤝 협력 구조
Client → Iterable.iterator() → Iterator
↓
hasNext() & next()
↓
실제 데이터
💪 실무에서의 활용
🔍 언제 사용할까?
- 모든 요소 순회가 필요할 때
- 자료구조에 독립적인 알고리즘 작성
- 안전한 순회가 필요할 때 (ConcurrentModificationException 방지)
⚖️ Iterator vs 인덱스 접근
// ✅ Iterator 사용 (권장)
for (String item : list) {
System.out.println(item);
}
// ❌ 인덱스 사용 (비권장)
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i)); // LinkedList에서 O(n) 성능!
}
🎯 오늘의 핵심 포인트
- 통일된 순회: Iterator 패턴으로 모든 자료구조를 동일하게 순회
- 캡슐화의 힘: 내부 구조를 몰라도 순회 가능
- for-each의 비밀: 컴파일러가 Iterator로 자동 변환
- 다형성 활용: 인터페이스 기반으로 유연한 코드 작성
- 성능 고려: LinkedList에서 인덱스 접근 대신 Iterator 사용
'🧑💻Sparta' 카테고리의 다른 글
| TIL - Collection (09.26) (0) | 2025.09.26 |
|---|---|
| TIL - Comparator (09.25) (0) | 2025.09.25 |
| TIL - Deque (09.23) (0) | 2025.09.23 |
| TIL - Queue (09.22) (0) | 2025.09.23 |
| TIL - Stack (09.21) (0) | 2025.09.23 |