본문 바로가기
프로그래밍/Kotlin

Kotlin] for문 vs foreach문

by Hwan2 2020. 7. 18.
반응형

1. for문과 foreach문

fun main() {
for (i in 1..10)
println(i) //output : 1, 2, 3, 4, ... 10

(1..10).forEach { i ->
println(i) //output : 1, 2, 3, 4, ... 10
}
}

일반적인 for문과 forEach문 사용방법입니다.


모든 작업은 for문으로 대체가 가능합니다.


그럼에도 불구하고 forEach문을 사용하는 이유는 무엇일까요??


2. Performance

1) 일반적인 반복문에 대한 시간 계산

fun loop(i: Int){
for(i in 0..i){}
}

fun main() {
println("ForLoop Time: " + measureNanoTime {
for (i in 0..10000) { loop(i) }
})

println("ForEach Time: " + measureNanoTime {
(0..10000).forEach { i -> loop(i) }
})
}


일반적인 반복문에선 for문이 우세합니다.

이 이유는 단순 계산작업일 뿐더러 forEach문은 람다형식으로 함수를 계속 호출하는 형태이므로 퍼포먼스가 저하가됩니다.


2) Collection 의 반복문 시간 계산

fun loop(i: Int){
for(i in 0..i){}
}

fun main() {
val list = (1..10000).toList()

println("ForLoop Time: " + measureNanoTime {
for (i in list) { loop(i) }
})

println("ForEach Time: " + measureNanoTime {
list.forEach { i -> loop(i) }
})
}


자바의 자료구조에는 List, Set, Map이 있습니다.

이 클래스들은 Collection을 상속받고 있으며 해당 클래스들에 대한 몇몇 함수들은 inline을 제공하게 됩니다.

그 중 forEach도 포함되는데, 그덕에 for문보다 더 좋은 퍼포먼스를 낼 수 있습니다.



3) asSequence()

fun loop(i: Int){
for(i in 0..i){}
}

fun main() {
val list = (1..100000).toList()

println("ForLoop Time: " + measureNanoTime {
for (i in list) { loop(i) }
})

println("ForEach Time: " + measureNanoTime {
list.forEach { i -> loop(i) }
})

list.asSequence()
println("ForEach Time: " + measureNanoTime {
list.forEach { i -> loop(i) }
})
}



좀 더 차이를 보기위해 반복횟수를 10,000 -> 100,000으로 변경했습니다.


asSequence() 함수를 통해 list호출시 최적화를 진행할 수 있습니다.


asSequnece()는 lazy collection의 한 종류인데, 체인 형식으로 collection이 호출될 경우(list -> filter -> map)

list는 값들을 저장하기위해 temp collection을 따로 만든 후 값을 저장하는 형태를 띄우게 됩니다.

결국 이는 오버헤드로 이어지며 퍼모먼스가 저하됩니다.


하지만 Java 8 에서는 stream에 대응되는 lazy collection이 제공되므로 이를 해결할 수 있게 되었습니다.


4) run

Kotlin에서는 for문의 break 문과 비슷한 방식으로 forEach문을 만들 수 있습니다.

import kotlin.system.measureNanoTime

fun loop(i: Int){
for(i in 0..i){}
}

fun main() {
val list = (1..10000).toList()

println("ForEach Time: " + measureNanoTime {
list.forEach { i -> loop(i) }
})

println("ForEach Time: " + measureNanoTime {
list.forEach { i -> if(i > 5000) return@forEach
else loop(i) }
})

println("ForEach Time: " + measureNanoTime {
run loop@{
list.forEach { i -> if(i > 5000) return@loop
else loop(i)
}
}
})
}



두번째의 return@forEach문은 for문에서의 continue와 같은 역할을 합니다.

즉, 중간에 break가 되지 않았습니다.(여기서 @forEach는 list.forEach의 forEach입니다.)


세번째 return@loop는 run이라는 람다함수를 제공함으로 써 아에 forEach문 loop를 빠져나간 것입니다.

때문에 for문의 break역할을 하게 됩니다.

반응형

댓글


스킨편집 -> html 편집에서