관리 메뉴

나만을 위한 블로그

[Kotlin] 컬렉션 정렬 함수 정리 본문

개인 공부/Kotlin

[Kotlin] 컬렉션 정렬 함수 정리

참깨빵위에참깨빵 2022. 12. 21. 20:03
728x90
반응형

코틀린에는 Immutable, mutable 2가지 속성이 있고 컬렉션도 이 속성에 따라 2가지로 나눠진다. 이 포스팅에선 리스트를 통해 코틀린에 존재하는 컬렉션 정렬 함수를 확인한다.

 

sort() / sorted()

 

먼저 sort()와 sorted()다. 두 함수는 하는 일은 같은 것 같은데 이름이 나눠져 있다. 각 함수에 대한 코틀린 공식 홈페이지의 설명은 아래와 같다.

 

https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/sort.html

 

sort - Kotlin Programming Language

 

kotlinlang.org

배열을 제자리에서 정렬한다

 

https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.sequences/sorted.html

 

sorted - Kotlin Programming Language

 

kotlinlang.org

자연 정렬 순서에 따라 정렬된 이 시퀀스의 요소를 생성하는 시퀀스를 반환한다. 정렬이 안정적이다. 이는 동일한 요소가 정렬 후 서로 상대적인 순서를 유지함을 의미한다. 작업은 중간 상태(intermediate)이며 상태 저장(stateful)이다

 

설명을 봐도 애매하다. IDE에서 sort()와 sorted()의 원형을 확인하면 두 함수의 위치와 리턴형이 다른 걸 확인할 수 있다.

 

/**
 * Sorts elements in the list in-place according to their natural sort order.
 *
 * The sort is stable. It means that equal elements preserve their order relative to each other after sorting.
 *
 * @sample samples.collections.Collections.Sorting.sortMutableList
 */
public actual fun <T : Comparable<T>> MutableList<T>.sort(): Unit {
    if (size > 1) java.util.Collections.sort(this)
}


/**
 * Returns a list of all elements sorted according to their natural sort order.
 * 
 * The sort is stable. It means that equal elements preserve their order relative to each other after sorting.
 */
public fun <T : Comparable<T>> Iterable<T>.sorted(): List<T> {
    if (this is Collection) {
        if (size <= 1) return this.toList()
        @Suppress("UNCHECKED_CAST")
        return (toTypedArray<Comparable<T>>() as Array<T>).apply { sort() }.asList()
    }
    return toMutableList().apply { sort() }
}

 

sort()는 _Collections.kt에 속하고 sorted()는 MutableCollectionsJVM.kt에 속한다.

그리고 sort()는 아무것도 리턴하지 않지만 sorted()는 List<T>를 리턴한다. IDE에서 대충 리스트를 만든 뒤 두 함수를 사용하면 더 확실하게 알 수 있다.

 

val list = arrayListOf(2, 4, 1, 3, 5)

val a = list.sort()
println(a)	// kotlin.Unit
val b = list.sorted()
println(b)	// [1, 2, 3, 4, 5]

 

제자리에서 정렬한다는 것은 정렬 후 리턴값이 없다는 뜻으로 보면 될 것 같다.

sort()를 먹인 list 안의 값을 출력하려면 아래와 같이 하면 된다.

 

var list = arrayListOf(2, 4, 1, 3, 5)

list.sort()
println(list)

list = arrayListOf(2, 4, 1, 3, 5)
val a = list.sorted()
println(a)

 

위와 같이 하면 list, a 모두 정렬된 값들을 출력하는 걸 볼 수 있다.

두 함수의 좀 더 정확한 용례는 아래의 블로그에서 설명하고 있다.

 

https://velog.io/@changhee09/Kotlin-%EB%A6%AC%EC%8A%A4%ED%8A%B8-%EC%A0%95%EB%A0%ACsort-sortBy-sortWith

 

[Kotlin] 리스트 정렬(sort, sortBy, sortWith)

코틀린 - 정렬(sort, sortBy, sortWith)

velog.io

sorted()는 데이터 변경이 안 되는 Immutable List를 정렬 시 사용한다. sorted()는 리스트의 원본을 변경하지 않고 정렬된 리스트를 생성해서 리턴한다...(중략)...sort()는 데이터 변경이 가능한 Mutable List를 정렬할 때 사용한다. 리스트 자신이 가진 요소의 순서를 변경한다.

 


 

reverse() / reversed() / asReversed()

 

순차 정렬을 확인했으니 이번엔 역순 정렬이다. 그런데 역순 정렬 함수는 3종류가 있다.

 

fun main() {
    var list = arrayListOf(2, 4, 1, 3, 5)

    list.sort()
    list.reverse()
    println(list)

    list = arrayListOf(2, 4, 1, 3, 5)
    val a = list.sorted().reversed()
    println(a)

    list = arrayListOf(2, 4, 1, 3, 5)
    val b = list.sorted().asReversed()
    println(b)
}

// [5, 4, 3, 2, 1]
// [5, 4, 3, 2, 1]
// [5, 4, 3, 2, 1]

 

reverse()와 reversed()는 각각 Immutable, Mutable에 따라 용법이 달라지는 건 알겠는데 asReversed()는 무엇인가?

 

https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/as-reversed.html

 

asReversed - Kotlin Programming Language

 

kotlinlang.org

원래 리스트의 반전된 읽기 전용 보기(read-only view)를 반환한다. 원래 목록의 모든 변경사항은 반전된 리스트에 반영된다

 

두 함수만 있어도 충분할 것 같은데 asReversed()가 필요한 이유는 뭘까?

 

https://stackoverflow.com/questions/57115611/asreversed-vs-reversed-in-kotlin

 

asReversed() vs reversed() in Kotlin?

I noticed that Kotlin has two built in methods reversed() and asReversed() Is there any difference between these two? or are they essentially just doing the exact same thing?

stackoverflow.com

reversed() : 요소가 역순으로 있는 List만 반환한다. 배열, 리스트 같은 다른 객체에 이 확장에 대한 여러 정의가 있다
asReversed() : List에만 적용 가능하며 원래 리스트의 반전된 읽기 전용 보기를 반환한다. 원래 리스트의 모든 변경사항은 반전된 목록에 반영된다

 

이 답변대로 reverse()와 reversed()는 여러 배열에 사용할 수 있지만 asReversed()는 List에만 적용할 수 있다. 아래는 asReversed()의 원형이다.

 

public fun <T> List<T>.asReversed(): List<T> = ReversedListReadOnly(this)

 


 

sortedWith() / sortWith()

 

sortedWith()은 Immutable List, sortWith()은 Mutable List에 사용할 수 있다.

맨 위의 sort()들과 다른 것은 sortedWith()은 Comparator라는 걸 사용해서 정렬 조건을 설정할 수 있다는 것이다.

 

fun main() {
    val list = listOf("aaaaa", "bbbb", "ccc", "dd", "e")
    val comparator: Comparator<String> = compareBy { it.length }
    val lengthOrder = list.sortedWith(comparator)
    println("문자열 길이 순 정렬 : $lengthOrder")
}

// 문자열 길이 순 정렬 : [e, dd, ccc, bbbb, aaaaa]

 

문자열 뿐 아니라 객체도 정렬할 수 있다. 아래는 월별로 분류하고 나서 일별로 분류하는 예제다. comparator 프로퍼티를 만들지 않고 sortWith() 안에서 만들어 사용한다.

 

fun main() {
    val dates = mutableListOf(
        Dates(8, 19),
        Dates(5, 16),
        Dates(1, 29),
        Dates(5, 10),
        Dates(8, 3),
    )
    dates.sortWith(compareBy<Dates> { it.month }.thenBy { it.day })
    dates.forEach {
        println(it)
    }
}

data class Dates(
    val month: Int,
    val day: Int
)

/* 
Dates(month=1, day=29)
Dates(month=5, day=10)
Dates(month=5, day=16)
Dates(month=8, day=3)
Dates(month=8, day=19)
*/

 

반응형
Comments