이번에는 Kotlin의 Collection과 Sequence에서 사용할 수 있는 filter, map 함수의 호출 순서에 따른 성능 차이에 대해 간단하게 다뤄보려고 한다.

map, filter vs filter, map

  • 사용될 data class를 정의한다.
1
2
3
4
data class Person(
val name: String,
val age: Int
)
  • 사용될 list는 동일하다.
  • 먼저, map() 함수를 수행한 뒤, filter() 함수를 수행한다.
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
32
33
34
35
fun testMapAndFilter() {
val list = listOf(
Person("Alice", 27),
Person("hzoou", 25),
Person("txxbro", 28),
Person("iyj", 28),
Person("WooVictory", 27))

val result = list.map(::mapToName)
.filter(::filterByAge)
println(result)
}

fun mapToName(person: Person): Int {
println("map : ${person.name}")
return person.age
}

fun filterByAge(age: Int): Boolean {
println("filter : $age")
return age > 27
}

// Result
map : Alice
map : hzoou
map : txxbro
map : iyj
map : WooVictory
filter : 27
filter : 25
filter : 28
filter : 28
filter : 27
[28, 28]
  • map()으로 인해 5번, filter()로 인해 5번 => 총 10회
  • 다음으로는 filter() 함수를 수행한 뒤, map() 함수를 수행한다.
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
32
33
fun testFilterAndMap() {
val list = listOf(
Person("Alice", 27),
Person("hzoou", 25),
Person("txxbro", 28),
Person("iyj", 28),
Person("WooVictory", 27))

val result = list.filter { filterByAge(it.age) }
.map(::mapToName)

println(result)
}

fun mapToName(person: Person): Int {
println("map : ${person.name}")
return person.age
}

fun filterByAge(age: Int): Boolean {
println("filter : $age")
return age > 27
}

// Result
filter : 27
filter : 25
filter : 28
filter : 28
filter : 27
map : txxbro
map : iyj
[28, 28]
  • filter()로 인해 5번, filter()의 결과를 map()한 결과로 인해 2번 => 총 7회

연산의 순서를 변경함으로써 수행되는 연산의 횟수를 줄여 성능을 개선할 수도 있다. 연산의 순서를 항상 바꿀 수 있는 것은 아니지만, 위의 경우처럼 변경이 가능한 경우에는 원소를 줄여주는 연산을 먼저 수행하면 전체 연산의 수를 줄일 수 있다.

이는 collection, sequence 모두 해당된다.
하지만, 이 경우에는 collection 연산은 여전히 intermediate collection을 생성한다는 문제가 존재한다.

Reference