Giả sử tôi có một class Foo(val a: String, val b: Int, val c: Date)
và tôi muốn sắp xếp danh sách các Foo
s dựa trên cả ba thuộc tính. Làm thế nào tôi sẽ đi về điều này?
Giả sử tôi có một class Foo(val a: String, val b: Int, val c: Date)
và tôi muốn sắp xếp danh sách các Foo
s dựa trên cả ba thuộc tính. Làm thế nào tôi sẽ đi về điều này?
Câu trả lời:
Kotlin's stdlib cung cấp một số phương pháp trợ giúp hữu ích cho việc này.
Đầu tiên, bạn có thể xác định một bộ so sánh bằng cách sử dụng compareBy()
phương thức và chuyển nó vào sortedWith()
phương thức mở rộng để nhận một bản sao danh sách được sắp xếp:
val list: List<Foo> = ...
val sortedList = list.sortedWith(compareBy({ it.a }, { it.b }, { it.c }))
Thứ hai, bạn có thể cho phép Foo
triển khai Comparable<Foo>
bằng cách sử dụng compareValuesBy()
phương thức trợ giúp:
class Foo(val a: String, val b: Int, val c: Date) : Comparable<Foo> {
override fun compareTo(other: Foo)
= compareValuesBy(this, other, { it.a }, { it.b }, { it.c })
}
Sau đó, bạn có thể gọi sorted()
phương thức mở rộng không có tham số để nhận một bản sao danh sách đã được sắp xếp:
val sortedList = list.sorted()
Nếu bạn cần sắp xếp tăng dần trên một số giá trị và giảm dần trên các giá trị khác, stdlib cũng cung cấp các hàm cho điều đó:
list.sortedWith(compareBy<Foo> { it.a }.thenByDescending { it.b }.thenBy { it.c })
Các vararg
phiên bản của compareValuesBy
không inlined trong bytecode nghĩa lớp nặc danh sẽ được tạo ra cho các lambdas. Tuy nhiên, nếu bản thân các lambdas không nắm bắt trạng thái, các thể hiện singleton sẽ được sử dụng thay vì khởi tạo lambdas mọi lúc.
Như Paul Woitaschek đã lưu ý trong các nhận xét, so sánh với nhiều bộ chọn sẽ khởi tạo một mảng cho lệnh gọi vararg mọi lúc. Bạn không thể tối ưu hóa điều này bằng cách giải nén mảng vì nó sẽ được sao chép trong mọi cuộc gọi. Mặt khác, những gì bạn có thể làm là trích xuất logic vào một thể hiện so sánh tĩnh và sử dụng lại nó:
class Foo(val a: String, val b: Int, val c: Date) : Comparable<Foo> {
override fun compareTo(other: Foo) = comparator.compare(this, other)
companion object {
// using the method reference syntax as an alternative to lambdas
val comparator = compareBy(Foo::a, Foo::b, Foo::c)
}
}
ANEWARRAY kotlin/jvm/functions/Function1
compareBy
với nhiều lambdas sẽ không phân bổ mảng mới cho mỗi lần compareTo
gọi.
Nếu bạn muốn sắp xếp theo thứ tự giảm dần, bạn có thể sử dụng câu trả lời được chấp nhận:
list.sortedWith(compareByDescending<Foo> { it.a }.thenByDescending { it.b }.thenByDescending { it.c })
Hoặc tạo một chức năng mở rộng như compareBy
:
/**
* Similar to
* public fun <T> compareBy(vararg selectors: (T) -> Comparable<*>?): Comparator<T>
*
* but in descending order.
*/
public fun <T> compareByDescending(vararg selectors: (T) -> Comparable<*>?): Comparator<T> {
require(selectors.size > 0)
return Comparator { b, a -> 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
}
và sử dụng: list.sortedWith(compareByDescending ({ it.a }, { it.b }, { it.c }))
.
Nếu bạn cần sắp xếp theo nhiều trường và một số trường bằng cách giảm dần và những trường khác bằng cách tăng dần, bạn có thể sử dụng:
YOUR_MUTABLE_LIST.sortedWith(compareBy<YOUR_OBJECT> { it.PARAM_1}.thenByDescending { it.PARAM_2}.thenBy { it.PARAM_3})