Kotlin
[Effective Kotlin] 50 - 컬렉션 처리 단계 수를 제한하라
매운돌
2024. 1. 7. 22:58
모든 컬렉션 처리 메서드는 비용이 많이 듭니다. 표준 컬렉션 처리는 내부적으로 요소들을 활용해 반복을 돌며, 내부적으로 계산을 위해 추가적인 컬렉션을 만들어 사용합니다. 시퀀스 처리도 시퀀스 전체를 랩하는 객체가 만들어지며, 조작을 위해서 또 다른 추가적인 객체를 만들어 냅니다. 두 처리 모두 요소의 수가 많다면, 꽤 큰 비용이 들어갑니다. 따라서 적절한 메서드를 활용해서, 컬렉션 처리 단계 수를 적절하게 제한하는 것이 좋습니다.
class Student(val name: String?)
// 작동은 합니다.
fun List<Student>.getNames(): List<String> = this
.map { it.name }
.filter { it != null }
.map { it!! }
// 더 좋습니다.
fun List<Student>.getNames(): List<String> = this
.map { it.name }
.filterNotNull()
// 가장 좋습니다.
fun List<Student>.getNames(): List<String> = this
.mapNotNull { it.name }
사실 컬렉션 처리와 관련해서 비효율적인 코드를 작성하는 이유는 그것이 필요 없다고 생각해서가 아니라, 어떤 메서드가 있는지 몰라서인 경우가 많습니다. 따라서 어떤 메서드가 있는지 확인해 보는 것이 좋습니다. 참고로, 상황에 따라서 경고로 어떤 메서드를 사용하는 것이 좋은지 어느 정도 알려 줍니다.
경고로 어느 정도 알려 준다고 해도, 컬렉션 처리를 어떤 형태로 줄일 수 있는지 알아두면 좋습니다. 다음 표는 두 단계 이상의 컬렉션 처리 함수를 한번에 끝내는 방법을 정리한 것입니다.
이 코드보다는 | 이 코드가 좋습니다. |
.filter { it != null } .map { it!! } |
.filterNotNull() |
.map{ <Transformation> } .filterNotNull() |
.mapNotNull{ <Transformation> } |
.map { <Transformation> } .joinToString() |
.joinToString{ <Transformation> } |
.filter { <Predicate 1> } .filter { <Predicate 1> } |
.filter { <Predicate 1> && <Predicate 2> } |
.filter { it is Type } .map { it as Type } |
.filterIsInstance<Type>() |
.sorteBy { <Key1> } .sorteBy { <Key2> } |
.sortedWith( compareBy({ <Key 1> }, {<Key 2> })) |
listOf(...).filterNotNull() |
listOfNotNull(...) |
.withIndex() .filter { {index, elem} -> <Predicate using index> } .map { it.value } |
.filterIndexed { index, elem -> <Predicate using index>} (map, forEach, reduce, fold도 비슷합니다.) |