[Kotlin]Collection Function

Hood·2024년 8월 28일

Kotlin

목록 보기
6/18
post-thumbnail

✍ 코틀린과 친해지자

PS 문제를 하나씩 풀다 보니, 공부가 필요하다고 느낀 문법을 정리한 글입니다.


🔎 Collection 함수

지난 포스트에서 Collection은 여러 데이터를 편하게 다루기 위해 제공되는 라이브러리라고 정리했습니다.
이번 글에서는 Collection에 들어 있는 데이터를 더 쉽게 가공하고 처리할 때 자주 사용하는 함수들을 알아보겠습니다.

컬렉션을 다루다 보면 정렬, 변환, 반복, 필터링, 그룹화 같은 작업이 자주 필요합니다.
Kotlin은 이런 작업을 간결하게 처리할 수 있도록 다양한 함수를 제공합니다.


sorted / sortedDescending

컬렉션에 데이터가 들어 있다면, 원하는 기준에 따라 정렬하고 싶을 때가 많습니다.
이럴 때 사용하는 함수가 sorted()sortedDescending()입니다.

  • sorted()는 오름차순으로 정렬된 새로운 컬렉션을 반환합니다.
  • sortedDescending()은 내림차순으로 정렬된 새로운 컬렉션을 반환합니다.
fun main() {
    val a = listOf(2, 3, 1)
    println(a.sorted())
    println(a.sortedDescending())

    val numbersMap = mapOf("key1" to 2, "key2" to 3, "key3" to 1)
    println(numbersMap.toSortedMap())
}
[1, 2, 3]
[3, 2, 1]
{key1=2, key2=3, key3=1}

위 예제에서 sorted()는 리스트를 오름차순으로 정렬한 결과를 반환하고,
sortedDescending()은 내림차순 결과를 반환합니다.

또한 MaptoSortedMap()을 사용하면 키를 기준으로 정렬된 Map을 얻을 수 있습니다.
예제에서는 키가 String이므로 문자열 기준 오름차순으로 정렬됩니다.


map

map()은 컬렉션의 각 요소를 원하는 형태로 변환할 때 사용하는 함수입니다.
기존 컬렉션을 바꾸는 것이 아니라, 변환된 결과를 담은 새로운 컬렉션을 반환합니다.

fun main() {
    val a: List<Int> = listOf(1, 2, 3)
    val b = a.map { it * 10 }

    println(b)
    println(a.map { it * 10 })
}
[10, 20, 30]
[10, 20, 30]

위 예제에서는 a의 각 요소에 10을 곱한 새로운 리스트를 만들고 있습니다.
람다식 안의 it은 컬렉션의 각 요소를 의미하며, 이를 이용해 원하는 방식으로 값을 변환할 수 있습니다.


forEach / forEachIndexed

forEach()는 컬렉션의 요소를 하나씩 순회하면서 처리할 때 사용합니다.
forEachIndexed()는 여기에 인덱스 정보까지 함께 사용할 수 있게 해줍니다.

fun main() {
    val a: List<String> = listOf("abc", "def", "hij")

    a.forEach { println(it) }
    a.forEachIndexed { index, value ->
        println("$index : $value")
    }
}
abc
def
hij
0 : abc
1 : def
2 : hij

forEach()는 값만 하나씩 처리하고,
forEachIndexed()는 값과 함께 인덱스도 사용할 수 있다는 차이가 있습니다.
인덱스는 리스트와 마찬가지로 0부터 시작합니다.


filter

filter()는 특정 조건을 만족하는 요소만 골라서 새로운 컬렉션으로 반환하는 함수입니다.

예를 들어 정수 리스트에서 짝수만 추려내고 싶다면 다음과 같이 사용할 수 있습니다.

fun main() {
    val a: List<Int> = listOf(1, 2, 3, 4, 5, 6)
    val b = a.filter { it % 2 == 0 }

    println(b)
}
[2, 4, 6]

위 코드에서는 각 요소를 2로 나누었을 때 나머지가 0인 값만 남기고 있습니다.
즉, filter()는 조건에 맞는 데이터만 걸러내는 데 유용합니다.


filterValues

filterValues()Map에서 value를 기준으로 필터링할 때 사용하는 함수입니다.
조건을 만족하는 value를 가진 key-value 쌍만 포함하는 새로운 Map을 반환합니다.

fun main() {
    val map = mapOf("a" to 1, "b" to 2, "c" to 1, "d" to 3)
    val filteredMap = map.filterValues { it > 1 }

    println(filteredMap)
}
{b=2, d=3}

위 예제에서는 value가 1보다 큰 데이터만 남겼기 때문에
b=2, d=3만 포함된 새로운 Map이 반환됩니다.


find / findLast

find()는 조건에 맞는 요소 중 가장 먼저 발견된 값 하나를 반환합니다.
조건에 맞는 값이 없다면 null을 반환합니다.

반대로 findLast()는 조건에 맞는 요소 중 가장 마지막 값을 반환합니다.

fun main() {
    val a: List<Int> = listOf(1, 2, 3, 4, 5, 6)

    val b = a.find { it % 2 == 0 }
    val c = a.findLast { it % 2 == 0 }

    println("find : $b, findLast : $c")
}
find : 2, findLast : 6

위 코드에서는 짝수 조건을 만족하는 값 중
가장 먼저 찾은 값은 2, 가장 마지막 값은 6입니다.

이와 비슷한 함수로는 first(), last(), firstOrNull(), lastOrNull() 등이 있습니다.


any / all / none

이 함수들은 컬렉션이 특정 조건을 만족하는지 검사할 때 자주 사용됩니다.

  • any() : 조건을 만족하는 요소가 하나라도 있으면 true
  • all() : 모든 요소가 조건을 만족해야 true
  • none() : 조건을 만족하는 요소가 하나도 없으면 true
fun main() {
    val a: List<Int> = listOf(1, 2, 3, 4, 5, 6)

    if (a.any { it % 2 == 0 }) {
        println("짝수 데이터가 존재합니다.")
    }

    if (a.all { it % 2 == 0 }) {
        println("모든 데이터가 짝수입니다!")
    } else {
        println("홀수 데이터도 존재합니다.")
    }

    if (a.none { it > 7 }) {
        println("7보다 큰 데이터는 없습니다.")
    }
}
짝수 데이터가 존재합니다.
홀수 데이터도 존재합니다.
7보다 큰 데이터는 없습니다.

any()는 “하나라도 있는가”,
all()은 “전부 만족하는가”,
none()은 “아예 없는가”를 확인하는 함수라고 생각하시면 이해하기 쉽습니다.


flatMap

flatMap()은 각 요소를 컬렉션으로 변환한 뒤,
그 결과를 하나의 컬렉션으로 평탄화해서 반환하는 함수입니다.

fun main() {
    val a: List<Int> = listOf(1, 2, 3)

    val flatA = a.flatMap { listOf(it * 3) }
    println(flatA)

    val flatList = a.flatMap { listOf(it * 3, it * 4) }
    println(flatList)
}
[3, 6, 9]
[3, 4, 6, 8, 9, 12]

첫 번째 예제는 각 요소를 하나의 값으로 변환했기 때문에 일반적인 map()과 비슷해 보입니다.
하지만 두 번째 예제처럼 각 요소를 여러 값으로 바꾸면, 그 결과를 한 번에 펼쳐서 하나의 리스트로 만들어줍니다.

즉, flatMap()변환과 평탄화를 동시에 수행하는 함수입니다.


getOrElse

getOrElse()는 지정한 인덱스의 값이 존재하면 그 값을 가져오고,
존재하지 않으면 기본값을 대신 반환하는 함수입니다.

fun main() {
    val a: List<Int> = listOf(1, 2, 3, 4, 5, 6)

    println(a.getOrElse(2) { 10 })
    println(a.getOrElse(10) {
        println("10번째 인덱스는 없습니다.")
        -1
    })
}
3
10번째 인덱스는 없습니다.
-1

첫 번째 결과는 인덱스 2가 실제로 존재하므로 해당 값인 3이 반환됩니다.
반면 두 번째 결과는 인덱스 10이 없기 때문에, 람다식 안의 코드가 실행되고 기본값 -1이 반환됩니다.

즉, getOrElse()는 없는 인덱스에 접근할 때 예외 대신 대체 값을 사용하고 싶을 때 유용합니다.


groupBy

groupBy()는 특정 기준에 따라 요소를 묶어서 Map 형태로 반환하는 함수입니다.
같은 기준을 가진 요소끼리 하나의 그룹으로 묶을 수 있습니다.

fun main() {
    val arr: List<String> = listOf("apple", "banana", "cherry", "date", "elderberry")

    val arrLength = arr.groupBy { it.length }
    println(arrLength)
}
{5=[apple], 6=[banana, cherry], 4=[date], 10=[elderberry]}

위 예제에서는 문자열의 길이를 기준으로 그룹화하였습니다.
그래서 길이가 6"banana""cherry"가 같은 그룹으로 묶여 있는 것을 확인할 수 있습니다.

groupBy()는 “같은 조건을 가진 데이터끼리 묶고 싶다”는 상황에서 매우 유용하게 사용할 수 있습니다.


📌 결론

이번 글에서는 Kotlin Collection에서 자주 사용하는 함수들을 정리해보았습니다.
컬렉션은 단순히 값을 저장하는 것에서 끝나지 않고,
정렬하고, 변환하고, 걸러내고, 검사하고, 그룹화하는 과정까지 자주 함께 사용됩니다.

이번에 정리한 함수들을 다시 보면 다음과 같습니다.

  • 정렬 : sorted(), sortedDescending()
  • 변환 : map(), flatMap()
  • 반복 : forEach(), forEachIndexed()
  • 필터링 : filter(), filterValues()
  • 탐색 : find(), findLast()
  • 조건 검사 : any(), all(), none()
  • 안전한 접근 : getOrElse()
  • 그룹화 : groupBy()

문제를 풀거나 실전 코드를 작성할 때 이런 함수들을 익혀두면
반복문을 길게 작성하지 않고도 훨씬 간결하게 데이터를 처리할 수 있습니다.
다음에는 Collection에서 자주 쓰이는 다른 함수들도 이어서 정리해보려고 합니다.

참고

  • Kotlin 공식 문서: Collections overview
  • Kotlin 공식 문서: Collection operations overview
profile
달을 향해 쏴라, 빗나가도 별이 될 테니 👊

0개의 댓글