Kotlin mapNotNull 완전 정복 | 리스트 변환부터 실무 활용까지
Kotlin을 사용하면서 가장 자주 마주치는 기능 중 하나가 바로 mapNotNull입니다.
특히, 리스트를 필터링하고 변환할 때 정말 유용하게 사용할 수 있는데요.
이 글에서는 mapNotNull의 기본 개념부터 다양한 실무 활용 예제까지 쉽게 풀어보겠습니다.
mapNotNull이란?
mapNotNull은 Kotlin 컬렉션에서 각 요소에 변환(transform) 함수를 적용한 뒤, null이 아닌 결과만 모아서 새로운 리스트로 반환하는 함수입니다. 간단한 예제를 볼까요?
val numbers = listOf(1, 2, 3, 4, 5, 6)
val doubled = numbers.mapNotNull { if (it % 2 == 0) it * 2 else null }
println(doubled) // [4, 8, 12]
짝수만 두 배로 변환하고, 나머지 홀수는 제외되었습니다. 이렇게 null을 걸러내는 처리를 깔끔하게 할 수 있습니다.
즉, 데이터를 변환하면서 동시에 null 값을 필터링하고 싶은 경우 매우 효율적인 도구입니다. map()과 filterNotNull()을 각각 사용하는 것보다 더 간단하고 직관적인 코드 작성을 가능하게 해줍니다.
map + filterNotNull()과의 차이점
mapNotNull은 다음의 코드를 단축시킨 형태입니다:
val result = list.map { it.toIntOrNull() }.filterNotNull()
위의 코드와 동일한 결과를 mapNotNull로 더 간단히 쓸 수 있죠.
val result = list.mapNotNull { it.toIntOrNull() }
가독성과 성능 면에서 모두 유리합니다. 특히, 중간 컬렉션이 생기지 않기 때문에 성능상으로도 이점이 있습니다.
다양한 컬렉션에서의 활용
List에서 사용하기
문자열 리스트에서 정수로 변환 가능한 값만 필터링하는 예제입니다:
val strings = listOf("12a", "45", "", "3")
val ints = strings.mapNotNull { it.toIntOrNull() }
println(ints) // [45, 3]
toIntOrNull()은 변환 실패 시 null을 반환하므로, mapNotNull로 처리하면 실패한 항목은 자동 제거됩니다.
Map에서 사용하기
Map의 각 항목을 조건에 따라 필터링해 리스트로 추출하는 예제입니다:
val map = mapOf("Alice" to 20, "Tom" to 13, "Bob" to 18)
val adults = map.mapNotNull { (name, age) -> name.takeIf { age >= 18 } }
println(adults) // [Alice, Bob]
takeIf는 조건이 true일 때 해당 값을 반환하고, false일 때 null을 반환합니다. 이와 mapNotNull을 함께 쓰면 매우 유용하죠.
Sequence에서 사용하기
Sequence는 lazy evaluation을 지원하여 큰 데이터셋 처리에 유리합니다:
val sequence = sequenceOf("1", "two", "3")
val validInts = sequence.mapNotNull { it.toIntOrNull() }.toList()
println(validInts) // [1, 3]
Flow(Coroutines)에서도 사용 가능
Flow에서도 동일한 방식으로 활용할 수 있으며, null을 안전하게 필터링하면서 변환이 가능합니다:
flowOf("10", "20", "x")
.mapNotNull { it.toIntOrNull() }
.collect { println(it) } // 10, 20
실무 팁: 이렇게 쓰면 좋아요
takeIf와 함께 조건 필터링
val numbers = listOf(1, 2, 3, 4, 5)
val filtered = numbers.mapNotNull { it.takeIf { it > 3 } }
println(filtered) // [4, 5]
takeIf는 조건에 맞는 값만 남기고, 아닌 경우 null을 반환하므로 mapNotNull과 궁합이 잘 맞습니다.
null 가능성 있는 람다 처리
val inputs = listOf("kotlin", null, "mapNotNull")
val upper = inputs.mapNotNull { it?.uppercase() }
println(upper) // [KOTLIN, MAPNOTNULL]
null-safe 연산자(?.)와 함께 쓰면, null을 걸러내면서도 예외 없이 안전하게 처리할 수 있습니다.
주의사항과 성능 이슈
- mapNotNull은 중간 컬렉션을 줄여주기 때문에 메모리 효율이 좋습니다.
- 하지만 map()과 filterNotNull()을 조합해도 충분할 경우, 코드 가독성과 의미가 더 잘 드러날 수 있습니다.
- Sequence나 Flow와 함께 사용하면 더욱 Lazy하게 작동합니다. 이는 대량의 데이터를 처리할 때 매우 유리합니다.
요약
항목 | 설명 |
목적 | 변환과 필터링을 한 번에 수행 |
대상 | List, Map, Sequence, Flow 등 거의 모든 컬렉션 |
장점 | null 자동 제거, 가독성, 성능 이점 |
주의 | 상황에 따라 map + filterNotNull 조합이 더 명확할 수 있음 |
mapNotNull은 단순한 함수처럼 보이지만, 쓰임새가 정말 다양하고 강력한 도구입니다.
이 글을 통해 여러분도 실무에서 더 유용하게 활용하실 수 있기를 바랍니다.
'개발&프로그래밍' 카테고리의 다른 글
[N8N] n8n If 노드 활용법 | contains·starts with·matches regex 예제 모음 (2) | 2025.06.20 |
---|---|
[N8N] n8n에서 루핑(Looping) 제대로 활용하는 방법 (0) | 2025.06.19 |
[Kotlin] runCatching + 코루틴 완벽 활용법! 예외 처리까지 깔끔하게 (1) | 2025.06.17 |
[Kotlin] runCatching 완벽 정리: 예외 처리도 간결하게! (2) | 2025.06.16 |
IntelliJ IDEA 2025.1 출시! 개발자 생산성을 높이는 주요 기능 총정리 (1) | 2025.04.23 |
댓글