조용한 담장

코틀린(Kotlin) Collections : Ordering 본문

kotlin

코틀린(Kotlin) Collections : Ordering

iosroid 2020. 4. 7. 16:08

kotlin

코틀린의 collection 의 Ordering 에 대해 살펴보자.

원문 https://kotlinlang.org/docs/reference/collection-ordering.html 을 보며 정리.

 

element 의 순서는 특정 collection type 에 따라서는 중요한 부분이다.

예를 들어 같은 element 를 가진 두개의 리스트는 element 의 order 에 따라 동일하지 않게 된다.

코틀린에서 object 의 order 는 다양한 방법으로 정의될 수 있다.

 

natural orderComparable interface 의 상속자를 위해 정의 되었다.

특별한 order 가 지정되지 않으면 기본으로 사용된다.

  • Numeric type 은 전통적인 수 기반 순서(numerical order)를 사용한다. 1 > 0, -3.4f > -5f

  • CharStringlexicographical order 를 사용한다. b > a, world > hello

개인이 정의한 type 에 natural order 를 정의하기 위해서는 Comparable 의 상속자가 되어야 하는데 이를 위해서는 compareTo() 함수를 구현해야 한다.

compareTo() 는 같은 타입의 다른 object 를 argument 로 받아야 하며 리턴하는 상수 값이 나타내는 더 큰 object 는:

  • Positive values show that the receiver object is greater.

  • Negative values show that it's less than the argument.

  • Zero shows that the objects are equal.

예제로 아래는 major, minor 로 구성된 version 을 ordering 하는데 사용하는 class 이다.

class Version(val major: Int, val minor: Int): Comparable<Version> {
    override fun compareTo(other: Version): Int {
        if (this.major != other.major) {
            return this.major - other.major
        } else if (this.minor != other.minor) {
            return this.minor - other.minor
        } else return 0
    }
}

fun main() {    
    println(Version(1, 2) > Version(1, 3))
    println(Version(2, 0) > Version(1, 5))
}

// output:
// false
// true

첫번째 println 은 minor 결과 값 2-3 이 음수이고 argument 인 Version(1, 3) 보다 작으므로 false 가 된다.

두번째 println 은 major 결과 값 2-0 이 양수이고 receiver object 인 Version(2, 0) 이 더 크므로 true 가 된다.

 

custom order 는 어느 타입의 인스턴스를 가지고 원하는 방식으로 sorting 을 할수 있게 한다.

non-comparable object 에 적용할 order 를 정의 하거나 comprable type 에 적용되는 natural order 외의 것을 정의할 수 있다.

Comparatorcompare() 함수를 가지고 있고 한 클래스의 두개의 인스턴스를 받아 둘의 비교 결과를 상수로 리턴한다.

이 결과는 위의 compareTo() 설명과 동일하다.

val lengthComparator = Comparator { str1: String, str2: String -> str1.length - str2.length }
println(listOf("aaa", "bb", "c").sortedWith(lengthComparator))

// output:
// [c, bb, aaa]

문자열의 기본인 lexicographical order 대신 lengthComparator 를 사용해 문자열의 길이를 기반으로 정열이 되었다.

compareBy() 를 사용하여 Comparator 를 간단하게 정의할 수 있다.

람다 함수를 인자로 받아 Comparable 값을 생성하며 이를 기반으로 custom order 를 구현하게 된다.

println(listOf("aaa", "bb", "c").sortedWith(compareBy { it.length }))

// output:
// [c, bb, aaa]

이번에 다루는 sorting 함수들은 read-only collection 에 적용되는 것들이며 그 결과로 새로운 collection 을 생성한다.

mutable collection 의 sorting 함수들은 List Specific Operations 에서 다룬다.

Natural order

sorted()sortedDescending() 는 natural order 에 따라 ascending 와 descending sequence 로 정렬된 collection 을 리턴한다.

Comparable elements 로 구성된 collection 에 적용 가능하다.

val numbers = listOf("one", "two", "three", "four")

println("Sorted ascending: ${numbers.sorted()}")
println("Sorted descending: ${numbers.sortedDescending()}")

// output:
// Sorted ascending: [four, one, three, two]
// Sorted descending: [two, three, one, four]

Custom orders

sortedBy()sortedByDescending() 를 사용하여 custom order 와 sorting non-comparable object 를 할 수 있다.

val numbers = listOf("one", "two", "three", "four")

val sortedNumbers = numbers.sortedBy { it.length }
println("Sorted by length ascending: $sortedNumbers")
val sortedByLast = numbers.sortedByDescending { it.last() }
println("Sorted by the last letter descending: $sortedByLast")

// output:
// Sorted by length ascending: [one, two, four, three]
// Sorted by the last letter descending: [four, two, one, three]

sortedWith() 와 argument 인 Comparator 의 동작을 정의하여 custom order 를 구현한다.

val numbers = listOf("one", "two", "three", "four")
println("Sorted by length ascending: ${numbers.sortedWith(compareBy { it.length })}")

// output:
// Sorted by length ascending: [one, two, four, three]

Reverse order

reversed() 를 사용한다.

val numbers = listOf("one", "two", "three", "four")
println(numbers.reversed())

// output:
// [four, three, two, one]

reversed() 는 새로운 collection 을 결과로 생성하지만 asReversed() 를 사용하면 같은 collection 인스턴스의 reversed view 를 얻게되어 가벼운 동작을 구현한다.

original collection 이 mutable 이면 값의 변화가 reversed view 에도 영향을 준다.

val original = mutableListOf('a', 'b', 'c', 'd', 'e')
val originalReadOnly = original as List<Char>
val reversed = originalReadOnly.asReversed()

println(original) // [a, b, c, d, e]
println(reversed) // [e, d, c, b, a]

// changing the original list affects its reversed view
original.add('f')
println(original) // [a, b, c, d, e, f]
println(reversed) // [f, e, d, c, b, a]

original[original.lastIndex] = 'z'
println(original) // [a, b, c, d, e, z]
println(reversed) // [z, e, d, c, b, a]

Random order

shuffled() 를 사용한다.

val numbers = listOf("one", "two", "three", "four")
println(numbers.shuffled())
Comments