JAVA for each 문의 특징
Table of contents
for-each문 핵심!
for-each문은 Iterable 인터페이스를 상속하는 자료구조에만 사용할 수 있다.
인덱스를 꼭 사용해야하는 로직이 아니라면 증감식을 사용하는 for문보다 for-each문을 사용하는 것이 성능상 유리하다.
for each 문이란?
String[] numbers = {"one", "two", "three"};
// 일반 for loop 문
for(int i=0; i<numbers.length; i++) {
System.out.println(numbers[i]);
}
// for each 문
for(String number: numbers) {
System.out.println(number);
}
for each문은 카운터 변수와 증감식을 사용하지 않는 loop 문이다.
인덱스를 명시할 필요 없이 자료구조의 사이즈만큼 반복되기 때문에 코드가 간결해진다.
for-each문은 Iterable 인터페이스를 상속하는 자료구조에만 사용할 수 있다.
실제로, Collection 인터페이스를 보면 Iterable을 상속하고 있다.
따라서, 우리는 ArrayList나 LinkedList와 같은 자료구조에 for-each 문을 사용할 수 있는 것이다.
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);
}
}
더 나아가서 Interable 인터페이스를 확인해보면, Interator 인터페이스를 반환하는 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());
}
}
그리고 Iterator 인터페이스에는 hasNext() 메서드와 next() 메서드가 있고, 이를 사용해서 for-each문을 사용하는 것이다.
String[] numbers = {"one", "two", "three"};
// Iterable에 있는 iterator() 메서드로 Iterator를 생성한다.
Iterator<String> iterator = list.iterator();
// Iterator의 hasNext()로 다음 원소가 있는지 확인한다. 다음 원소가 없을때까지 loop문을 반복한다.
while (iterator.hasNext()) {
// next() 메서드로 요소들을 순회한다.
System.out.println(iterator.next());
}
for-each문의 동작 원리는 위와 같다.
for-each문의 장점
public class Test {
public void printAll(List<Integer> list) {
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
public void printAll2(List<Integer> list) {
for (Integer integer : list) {
System.out.println(integer);
}
}
}
위와 같이 List<Integer> list
를 파라미터로 받아 리스트 내의 원소를 출력하는 메서드가 있다고 생각해보자.
두 메서드가 하는 역할이 같지만, 어떤 것을 사용하는 것이 좋을까?
답은 for-each문을 사용한 두번째 메서드이다.
왜냐하면, 우리는 List<Integer> list = new ArrayList<>();
혹은 List<Integer> list = new LinkedList<>();
객체를 생성할 수 있고, 위 메서드를 사용할 수 있을것이다.
ArrayList 자료구조의 경우, 첫번째 메서드 처럼 인덱스를 사용하는 for 문을 사용할 때 해당 인덱스로 바로 접근할 수 있어 두 메서드의 성능차이가 크게 없지만
LinkedList 자료구조의 경우, 인덱스를 사용해서 for문을 수행하게 되면 매번 첫 번째 Head에서 해당 인덱스까지 이동해야하기 때문에
해당 메서드의 경우 O(N^2) 시간 복잡도를 갖게 된다.
따라서, 성능과 범용성을 생각해서 Collection 자료구조를 다룰 때, 인덱스가 꼭 필요한 상황이 아니라면 for-each문을 사용하는 것이 좋다.
참고한 블로그