일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 가비지 컬렉션
- Varchar
- 스프링
- java
- gc
- 가비지 컬렉터
- text
- CAS
- Di
- db
- Atomic Type
- 동시성
- 자바
- 백엔드
- Synchronized
- 데이터 타입
- 동시성 문제
- iterator
- iterable
- Lock
- foreach
- MVCC
- Locking Read
- reflection
- jpa
- MySQL
- Today
- Total
과정을 즐기자
Java의 foreach 문의 내부 동작 방식 (feat. Iterator, Iterable) 본문
자바의 for 문에는 일반 for 문과 foreach (향상된 for 문)이 있습니다.
일반 for 문
for (int i = 0; i < 10; i++) {
System.out.println(members[i]);
}
foreach 문
for (Member member : members) {
System.out.println(member);
}
지금까지 foreach 문이 간결하고 가독성도 좋기 때문에 자주 사용했습니다.
하지만 foreach 문은 언제 사용할 수 있고 내부 동작 방식에 대해서 의문점이 생겼습니다.
이번 글에서는 foreach 문은 어떤 내부 동작 방식으로 인해 어떠한 조건일 때 사용할 수 있는 지에 대해 알아보겠습니다.
먼저 Iterator, Iterable 인터페이스에 대해 알아보겠습니다.
Iterator, Iterable 인터페이스
Iterator 인터페이스는 Collection에 저장된 요소를 읽어오는 것을 표준화한 인터페이스입니다.
Java에서 구현한 Iterator를 살펴보겠습니다.
public interface Iterator<E> {
boolean hasNext();
E next();
default void remove() {
throw new UnsupportedOperationException("remove");
}
default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
}
hasNext(), next()는 Iterator 구현시 직접 구현해야 하는 메소드이며 remove(), forEachRemaining은 default 메소드로
구현되어 있습니다.
Iterable 인터페이스는 하위 클래스에 Iterator를 반환하는 iterator() 메소드의 구현을 강제하는 역할을 합니다.
Java에서 구현한 Iterable을 살펴보겠습니다.
public interface Iterable<T> {
Iterator<T> iterator();
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
default Spliterator<T> spliterator() {
return Spliterators.spliteratorUnknownSize(iterator(), 0);
}
}
Iterator를 반환하는 iterator() 메소드를 구현해야 하며 forEach(), spliterator()는 default 메소드로 구현되어 있습니다.
foreach는 어떤 조건에 사용할 수 있나?
이제 Iterator, Iterable의 구현체를 직접 구현해보며 foreach를 어떤 조건에 사용할 수 있는 지 알아보겠습니다.
public class MyCollections<T> implements Iterable<T> {
private final T[] elements;
public MyCollections(T[] elements) {
this.elements = elements;
}
@Override
public Iterator<T> iterator() {
return new MyIterator();
}
private class MyIterator implements Iterator<T> {
private int index = 0;
@Override
public boolean hasNext() {
return index < elements.length;
}
@Override
public T next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return elements[index++];
}
}
}
Iterable 인터페이스를 구현하여 MyCollections 컬렉션을 구현하였고 iterator() 메소드를 필수적으로 구현해야 합니다.
iterator() 메소드를 구현하기 위해 MyIterator 클래스를 생성하고 다음 원소가 있는지 확인하는 hasNext(), 다음 원소를
반환하는 next() 메소드를 구현하였습니다.
@Test
@DisplayName("foreach를 사용하려면 Iterable을 구현해야 합니다")
void test() {
// given
Member member1 = new Member("이름1", 20);
Member member2 = new Member("이름2", 20);
Member member3 = new Member("이름3", 20);
Member member4 = new Member("이름4", 20);
Member member5 = new Member("이름5", 20);
Member[] members = new Member[]{member1, member2, member3, member4, member5};
// when
MyCollections<Member> myCollections = new MyCollections<>(members);
// then
for (Member memberCollection : myCollections) {
System.out.println(memberCollection.getName());
}
}
MyCollections는 foreach 문을 사용할 수 있습니다.
이렇게 사용할 수 있는 이유는 MyCollections은 Iterable을 구현하였고 Iterable은 Iterator를 반환하는 메소드가 있습니다.
Iterator에는 hasNext(), next() 메소드를 구현해 Collection에 저장된 요소를 읽어오는 것을 표준화 하였습니다.
내부적으로 Iterator에 구현된 메소드를 이용해서 foreach로 간결하게 표현할 수 있는 것입니다.
// foreach 문
for (Member memberCollection : myCollections) {
System.out.println(memberCollection.getName());
}
// foreach 문의 내부 동작 과정
Iterator<Member> iterator = myCollections.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
실제로 Java의 Collection 인터페이스는 Iterable 인터페이스를 상속받기 때문에 iterator() 메소드의 구현이 강제되어
foreach 문으로 사용할 수 있습니다.
public interface Collection<E> extends Iterable<E> {
...
Iterator<E> iterator();
...
}
정리
Iterator 인터페이스는 Collection에 저장된 요소를 읽어오는 것을 표준화하였고
Iterable 인터페이스는 하위 클래스에 Iterator를 반환하는 iterator() 메소드의 구현을 강제하는 역할을 합니다.
이 2가지 인터페이스를 이용해서 내부적으로 hasNext(), next() 메소드를 이용해서 간결하고 가독성이 좋은
foreach 문을 작성할 수 있습니다.
자바의 Collection은 Iterable를 상속받기 때문에 foreach 문을 사용할 수 있으며 직접 구현한 클래스에도 foreach 문을
사용하려고 한다면 Iterable, Iterator 인터페이스의 구현이 필요합니다.
참고한 자료
'Java' 카테고리의 다른 글
직렬화는 무엇이고 왜 필요한 것일까? (2) | 2023.11.08 |
---|---|
Java의 synchronized, Lock Stripping과 Atomic Type (0) | 2023.10.11 |
스프링 DI의 원리, JPA Entity의 기본 생성자가 필요한 이유 (feat. Java Reflection) (0) | 2023.09.19 |
가비지 컬렉션(feat. Paralle GC, CMS GC, G1 GC) (0) | 2023.09.14 |
자바 쓰레드 관리의 발전 (2) | 2023.08.18 |