web-dev-qa-db-ja.com

Kotlinの複数の値に基づいて/比較する方法は?

class Foo(val a: String, val b: Int, val c: Date)があり、3つのプロパティすべてに基づいてFoosのリストをソートするとします。これについてどうすればいいですか?

65
Kirill Rakhman

Kotlinのstdlibは、このための便利なヘルパーメソッドを多数提供しています。

最初に、 compareBy() メソッドを使用してコンパレータを定義し、それを sortedWith() 拡張メソッドに渡して、ソートされたコピーを受け取ることができますリストの:

_val list: List<Foo> = ...
val sortedList = list.sortedWith(compareBy({ it.a }, { it.b }, { it.c }))
_

第二に、 compareValuesBy() ヘルパーメソッドを使用して、Fooに_Comparable<Foo>_を実装させることができます。

_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 })
}
_

次に、パラメータなしで sorted() 拡張メソッドを呼び出して、リストのソートされたコピーを受け取ることができます。

_val sortedList = list.sorted()
_

選別方向

一部の値を昇順でソートし、他の値を降順でソートする必要がある場合、stdlibはそのための関数も提供します。

_list.sortedWith(compareBy<Foo> { it.a }.thenByDescending { it.b }.thenBy { it.c })
_

パフォーマンスに関する考慮事項

varargcompareValuesByバージョンはバイトコードにインライン化されません。つまり、ラムダに対して匿名クラスが生成されます。ただし、ラムダ自体が状態をキャプチャしない場合、毎回ラムダをインスタンス化する代わりにシングルトンインスタンスが使用されます。

コメントの Paul Woitaschek で述べたように、複数のセレクターと比較すると、vararg呼び出しの配列が毎回インスタンス化されます。呼び出しごとにコピーされるため、配列を抽出して最適化することはできません。一方、できることは、ロジックを静的コンパレータインスタンスに抽出して再利用することです。

_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)
    }
}
_
114
Kirill Rakhman