자료구조에 정렬 기준이 필요할 때
Comparator 라는 인터페이스 정의가 필요하다.
Comparator
Comprarator 는 인터페이스로 컬렉션을 정렬하는데 필요한 메소드를 정의하고있다.
기본 정렬기준 외에 다른 기준으로 정렬하고자 할 때 쓰인다.
아래 두 메소드는 이름은 다르지만 두 객체를 비교한다는 같은 기능을 목적으로 고안되었다.
compare
Comparator 의 유일한 추상 메소드로, 반드시 정의해줘야한다.
// 우선순위 비교
// a == b => 0
// a < b => Negative value
// a > b => Positive value
abstract fun compare(a: T, b: T): Int
Comparable
Comparator 를 커스텀 정렬 기준을 세울 때 구현한다면, Comparable은 기본 정렬기준을 구현하는데 사용한다.
[토막 상식] Interface 이름에는 ~able 이란 접미사가 자주 붙는데, 특정한 성질과 기능을 사용할 수 있음을 의미한다. 여기서의 Comparable 은 정렬이 가능함을 의미한다.
compareTo
Comparable 의 추상 메소드로 반드시 정의해줘야한다.
`compareTo()` 가 정의된 컬렉션은 자체적인 기본 정렬 기준을 비로소 가지게된다.
// 우선순위 비교
// a == b => 0
// a < b => Netgative value
// a > b => Positive value
abstract operator fun compareTo(other: T): Int
// Comparable 인터페이스 구현
class User(var name: String, var age: Int) : Comparable<User> {
// 이름 기준 오름차순 정렬
override fun compareTo(other: User): Int = this.name.compareTo(other.name)
}
fun main(){
val hecarim = User("Hecarim", 50)
val vladimir = User("Vladimir", 25)
val zinx = User("Zinx", 20)
// 기본 정렬 순서: Hecarim -> Vladimir -> Zinx
// sorted() 메소드 호출시 사전 정의한 인터페이스인 Comparable 에 의해 정렬된다.
val users = arrayOf(zinx, vladimir, hecarim).sorted()
for (user in users) {
println("${user.name} ${user.age}")
}
// [print]
// Hecarim 50
// Vladimir 25
// Zinx 20
}
// 이 때는 클래스에서 상속받은 Comparator 에 의한 기본 정렬 방식이 아닌
// 새로운 커스텀 정렬 기준을 생성해보자
// 이럴 때는 Comparator 를 람다식으로 생성하여 넘겨준다.
// 나이 오름차순
val users = arrayOf(zinx, vladimir, hecarim).sortedWith { prevUser, nextUser -> prevUser.age.compareTo(nextUser.age) }
for (user in users) {
println("${user.name} ${user.age}")
}
// [Print]
// Zinx 20
// Vladimir 25
// Hecarim 50
CompareBy
자주 사용되는 정렬 기준이 아니라면 여러 정렬 기준을 람다 표현식으로 전달할 수 있는 메소드다.
반환 타입은 Comparator .
// ..
val hecarim = User("Hecarim", 50)
val vladimir = User("Vladimir", 25)
val garen = User("Garen", 25)
val zinx = User("Zinx", 20)
// 1번째 정렬 기준: 나이
// 2번째 정렬 기준: 이름
val users = arrayOf(zinx, vladimir, garen, hecarim).sortedWith(compareBy({it.age}, {it.name}) )
for (user in users) {
println("${user.name} ${user.age}")
}
// [Print]
// Zinx 20
// Garen 25
// Vladimir 25
// Hecarim 50
간혹 orderBy 처럼 정렬 기준을 여러개로 하고싶을 수 있다.
예를 들면 (x,y,z) 좌표를 갖는 크기 3 고정의 IntArray 의 정렬 순서를
x만 내림차순, y,z 는 오름차순으로 정렬하는 로직을 구현해보자.
@Test
fun testSortedSetWithIntArray() {
// [정렬 기준]
// x: Descending sort
// y, z: Ascending sort
val point1 = intArrayOf(1, 1, 1)
val point3 = intArrayOf(3, 3, 1)
val point2 = intArrayOf(3, 2, 1)
val point4 = intArrayOf(5, 1, 3)
val point5 = intArrayOf(5, 2, 3)
val point6 = intArrayOf(7, 3, 3)
val point7 = intArrayOf(7, 1, 1)
// x 좌표만 내림차순으로
val pointRank = sortedSetOf<IntArray>(compareByDescending<IntArray> { it[0] }
// Comparator 가 여러개 필요할 때는 thenComparing 으로 체이닝할 수 있다.
.thenComparing ( compareBy({it[1]}, {it[2]}) ),
point1, point3, point2, point4,point5, point6, point7
)
for (point in pointRank) {
println("${point[0]} ${point[1]} ${point[2]}")
}
// [Print]
// 7 1 1
// 7 3 3
// 5 1 3
// 5 2 3
// 3 2 1
// 3 3 1
// 1 1 1
}
compareValuesBy
Comparable 내에서 여러 속성을 정렬기준으로 하고 싶은 경우 compareValuesBy 메소드를 사용할 수 있다.
data class Meeting(val startTime: Int, val endTime: Int) : Comparable<Meeting>{
override fun compareTo(other: Meeting): Int {
// endTime 오름차순 , endTime 이 같다면 startTime 오름차순
return compareValuesBy(this, other, {it.endTime}, {it.startTime})
}
}
class CompareTest {
@Test
fun testCompareValuesBy() {
val times = arrayOf(
"1 4", "3 5", "2 2",
"7 7",
"5 7", "1 2", "1 2",
"6 7", "8 11", "8 12",
"2 13", "12 14"
)
// 1. 종료시간으로 오름차순
val meetingSet = TreeSet<Meeting>()
for (time in times) {
val (startTime, endTime) = time.split(" ").map(String::toInt)
meetingSet.add(Meeting(startTime, endTime))
}
for (meeting in meetingSet) {
println(meeting)
}
}
}
실행 결과
내림차순을 도중에 섞어주고 싶다면?
부호 - 만 바꿔주면 비교 결과가 반전되므로 (오름차순 -> 내림차순) 이 된다.
data class Meeting(val startTime: Int, val endTime: Int) : Comparable<Meeting>{
override fun compareTo(other: Meeting): Int {
// endTime -> startTime (Descending)
return compareValuesBy(this, other, {it.endTime}, {-it.startTime})
}
}
🔗 Reference
'JVM > Kotlin' 카테고리의 다른 글
[Kotlin] HashMap, HashSet, LinkedHashSet (0) | 2022.09.17 |
---|---|
[Kotlin] SortedSet, NavigableSet, TreeSet (0) | 2022.09.16 |
[Java, Kotlin] equals, ==, === 비교 (0) | 2022.09.16 |
[Kotlin] Array SumOf, minOf, maxOf (0) | 2020.10.13 |
[Kotlin] Companion Object (static in Java) (0) | 2020.06.28 |