두 객체를 비교할 때 객체의 특정 원소를 통해 비교를 하려면 compareTo() 메소드를 통해 간단하게 비교할 수 있습니다. 또한, 이 compareTo() 메소드는 단순히 객체간의 비교 뿐만 아니라 List의 정렬에서도 사용됩니다.
자바와 코틀린에서는 이 compareTo() 메소드를 쉽게 사용할 수 있는 Comparable
인터페이스를 제공합니다.
Comparable<T>
package kotlin
/**
* Classes which inherit from this interface have a defined total ordering between their instances.
*/
public interface Comparable<in T> {
/**
* Compares this object with the specified object for order. Returns zero if this object is equal
* to the specified [other] object, a negative number if it's less than [other], or a positive number
* if it's greater than [other].
*/
public operator fun compareTo(other: T): Int
}
compareTo()의 정렬 기준
정렬은 compareTo()
의 값이 양수, 0, 음수인지를 가지고 정렬할지 말지를 결정합니다.
(단, 호출하는 객체의 값 - 파라미터 객체의 값인 경우입니다. 반대로 연산할 경우 결과도 반대입니다)
compareTo()의 값이 양수인 경우
- 호출하는 객체의 값이 더 크다는 것을 의미합니다.
- 호출하는 객체와 파라미터의 객체의 위치를 바꾸어 정렬합니다.
compareTo()의 값이 0인 경우
- 호출하는 객체와 파라미터의 객체의 값이 같다는 것을 의미합니다.
- 정렬하지 않습니다.
compareTo()의 값이 음수인 경우
- 호출하는 객체의 값이 더 작다는 것을 의미합니다.
- 정렬하지 않습니다.
compareValuesBy
public fun <T> compareValuesBy(a: T, b: T, vararg selectors: (T) -> Comparable<*>?): Int {
require(selectors.size > 0)
return compareValuesByImpl(a, b, selectors)
}
private fun <T> compareValuesByImpl(a: T, b: T, selectors: Array<out (T) -> Comparable<*>?>): Int {
for (fn in selectors) {
val v1 = fn(a)
val v2 = fn(b)
val diff = compareValues(v1, v2)
if (diff != 0) return diff
}
return 0
}
kotlin에서 제공하는 compareValuesBy()
를 통해 간단하게 compareTo()를 구현할 수 있습니다.
compareValuesBy()
에 비교할 두개의 객체 a, b와 가변인자 selectors를 받습니다.
selectors는 람다로 비교할 타입 T를 넘겨받아 Comparable을 리턴해주고, compareValuesBy()에서는 두개의 Comparable을 비교한 값이 0이 아니라면 return해주고, 0이라면 계속해서 다음 가변인자를 비교해줍니다.
이런식으로 클래스의 필드에 따라 우선순위를 두어 compareTo()를 구현할 수 있습니다.
예제 1.
Position을 Comparable 인터페이스를 상속받게 해주고
compareValuesBy()를 position의 x, y 순으로하여 compareTo()를 구성하게 해주고 정렬을 해줍니다.
import org.junit.jupiter.api.Test
data class Person(val position: Position)
data class Position(private val x: Int, val y: Int) : Comparable<Position> {
override fun compareTo(other: Position): Int {
return compareValuesBy(
this, other,
{
it.x
},
{
it.y
},
)
}
}
class ComparableTest {
@Test
fun comparableTest() {
val list = listOf(
Person(Position(0, 3)),
Person(Position(2, 2)),
Person(Position(1, 5)),
Person(Position(5, 1)),
Person(Position(4, 4)),
Person(Position(3, 0)),
)
val sortedList = list.sortedBy { it.position }
println(sortedList)
}
}
실행결과
x가 y보다 우선순위가 높으므로 x 오름차순으로 정렬되었습니다.
예제2
반대로 compareValuesBy()를 position의 y, x 순으로하여 compareTo()를 구성하게 해주고 정렬을 해줍니다.
import org.junit.jupiter.api.Test
data class Person(val position: Position)
data class Position(private val x: Int, val y: Int) : Comparable<Position> {
override fun compareTo(other: Position): Int {
return compareValuesBy(
this, other,
{
it.y
},
{
it.x
},
)
}
}
class ComparableTest {
@Test
fun comparableTest() {
val list = listOf(
Person(Position(0, 3)),
Person(Position(2, 2)),
Person(Position(1, 5)),
Person(Position(5, 1)),
Person(Position(4, 4)),
Person(Position(3, 0)),
)
val sortedList = list.sortedBy { it.position }
println(sortedList)
}
}
실행결과
y가 x보다 우선순위가 높으므로 y 오름차순으로 정렬된 결과를 확인할 수 있습니다.