본문으로 바로가기

Java for-loop vs Enhanced for-loop

category Java 2021. 9. 22. 15:52

알고리즘을 제외하고 다른 내용에 대해 포스팅을 하고 싶다고 생각해왔다. 

 

사실 알고리즘을 올리는 것도, 별도의 프로젝트를 진행하고 정말 짧은 짬이 날 때 일주일 동안 풀었던 문제들에 대해 가벼운 수준의 리팩토링과 주석 정도만을 달아서 글을 올린 수준이라, 추후 대대적인 글 정비가 필요하다고 생각한다.

 

여하튼 다른 내용에 대한 포스팅을 고민했고, 원래는 지난번 프로젝트 때 고민했던 DTO의 사용범위와 DDD에 대해 글을 적어볼까 했지만, 첫 글은 조금 가볍게 시작하는 것도 좋을 것 같아 이 주제를 선택했다.

 

이 주제에 대해 처음 고민한 것은, 모 강사가 나에게 이런 이야기를 해줬기 때문이다.

 

forEach는 향상된 for문이라고 불리우며, 이것은 성능상으로 '무조건적으로' 기존 for문에 비해서 우월하다. 따라서 항상 forEach문을 사용해야 한다.

 

이 이야기를 들었을 때 가장 먼저 들었던 생각은, 어? 아니지 않나? 였다.

 

내가 사용하는 언어인 Java의 경우, forEach가 의미하는 바가 여럿일 수 있다. 그리고 그중에 그 어느 것도, 성능 상으로 단순 for문에 비해서 '무조건' 낫다고 보기는 어려웠다. 그것에 의문을 품고 공부한 내용에 대해서 포스팅해보고자 한다.

 

우선은, 먼저 비교해볼 내용은 for-loop와 Enhanced for-loop(향상된 for문, Java5 추가)이다.

 

현재 이 글에서 저 둘이 의미하는 바는 이러하다. forEach에는 여러 종류가 있고, 그것들도 다룰 예정이므로 각각의 명칭을 다르게 지칭하려고 한다. 따라서 java 5에서 추가된 내부적으로 iterator를 사용하는 forEach 문을 Enhanced for-loop라고 표현하고, 추후에 나오는 forEach 문들은 다르게 표현하도록 하겠다.

 

두 for문의 예시

//for-loop
for(int i = 0; i<10; i++){
	//반복
}

//Enhanced for-loop
for(Integer i : IntegerList){
	//반복
}

 

지칭하는 글은 이 둘이다.

 

결론부터 얘기하자면, 이 둘의 성능 차이는 어마어마하게 크지는 않다. 그러나 분명하게 존재하고, 상황에 따라서 더 효율적인 선택을 하면 된다.

 

그 상황에 따른 반복문의 선택을 위해서 Enhanced for-loop의 내부 구조를 보겠다.

 

// iterable() 메서드로 iterable 인터페이스 객체 반환
Iterator iter = list.iterator();

while(iter.hasNext()){ // 순회할 객체가 남아있다면
	Integer num = (Integer)iter.next(); // 다음 객체 가져옴
    //...
}

우선적으로, Enhanced for-loop의 경우에는 내부적으로 Iterable 인터페이스를 구현한 객체만 사용할 수 있다. (Array로 실행되는 경우에는, 내부적으로 일반 for문으로 번역하여 돌려준다. 따라서 이 경우에는 Iterable 인터페이스가 없어도 사용 가능하다.)

 

당연하지만, 이렇게 내부적으로 메서드를 호출해야 하기 때문에, 자연스럽게 for문보다 forEach문이 비용이 더 비쌀 수밖에 없다.

 

따라서 일반적으로는 

 

기본 for-loop가 Enhanced for-loop에 비해서 속도는 빠르다고 정리할 수 있다. ( 모든 경우에 해당하지는 않음 )

 

미세하게라도 for-loop가 우월하니 그냥 있던 거 쓰면 되는 것 아닌가요?라고 생각할 수 있다. 그러나 

 

Enhanced for-loop는 가독성에서 굉장히 큰 장점이 있고, 인덱스를 명시할 필요가 없이 리스트 사이즈만큼 반복되기 때문에 에러의 여지도 적다. 

 

따라서,

 

Enhanced for-loop는 가독성, 안정성의 측면에서 장점이 있다.

 

그러나 이 둘은 할 수 있는 것과 할 수 없는 것이 명확하게 분리되어 있다.

 

내부적으로 iterator를 사용하기 때문에 발생하는 문제이기도 하며, 정리하자면 아래와 같다.

 

for-loop Enhanced for-loop
역순 정렬 가능, 인덱스를 원하는 대로 늘릴 수 있음.
(i = i+x)
항상 인덱스는 1씩 증가함
loop 동작 중 내부 요소 변경 가능 loop 동작 중 내부 요소 변경 불가

따라서 원하는 동작이 Enhanced for-loop로 할 수 없는 것이라면, 그때는 당연하지만 for-loop를 선택하는 것 이외의 선택지가 없다. 그러나 그렇지 않다면, Enhanced for-loop를 사용하는 것을 고려하는 것이 타당하다.

 

여기까지 정리되었다면, 아까 위에서 말했던 for-loop가 Enhanced for-loop에 비해 빠르지 않은 예외 케이스에 대해 궁금증이 생길 시점이라고 생각한다.

 

그것은 임의 액세스(Random Access)가 존재하지 않는 Collections의 경우, Enhanced for-loop가 더 빠르다는 점이다. (대표적으로는 LinkedList)

 

따라서 ArrayList나 Array의 경우에는 일반 for-loop가, LinkedList 등의 경우에는 Enhanced for-loop가 성능적으로 이점이 있다는 사실을 알 수 있습니다.

 

 

결론

 

'무조건'이라는 말은 굉장히 위험한 말이라고 생각한다. 만약 정말 Enhanced for loop가 모든 부분에서 기존 for-loop에 비해 우월했다면 기존의 for loop는 사라졌어야 했을 것이다. 그러나 현재까지도 남아있고 그것이 곧 그 말이 틀렸다는 것을 입증한다고 생각한다.

그러나 가독성이나 안정성은 굉장히 중요하고, for-loop에 비해 Enhanced for-loop의 가독성이나 안정성이 뛰어나다는 것 또한 명백하므로, 충분히 공부하고 고민해서 적절한 장소에 적절한 활용을 하면 좋지 않을까 생각한다.

다음 글로는 forEach(), stream().forEach(), parallelStream().forEach()에 대해 다룰까합니다.