Using comparator in kotlin
Solution 1
After considering the @Alexander answer, the code can be written as
private val MyCustomComparator = Comparator<MyObject> { a, b ->
when {
a == null && b == null -> return@Comparator 0
a == null -> return@Comparator -1
b == null -> return@Comparator 1
else -> return@Comparator 0
}
}
Solution 2
This can be done almost the same way as in Java:
private val myCustomComparator = Comparator<CustomObject> { a, b ->
when {
(a == null && b == null) -> 0
(a == null) -> -1
else -> 1
}
}
if else if else ...
was replaced by a single Kotlin when
in order make the code better readable.
In Kotlin sorting a list using a Comparator
can also be written like this:
val customObjects = listOf(CustomObject(), CustomObject())
customObjects.sortedWith(myCustomComparator)
Solution 3
As per other answers, a fairly direct translation lets you sort a list with e.g.:
fun myCustomComparator() = Comparator<CustomObject>{ a, b ->
when {
(a == null && b == null) -> 0
(a == null) -> -1
else -> 1
}
}
Now, there's nothing in here that depends upon your CustomObject
. So it's trivial to make it generic, so it can handle any type:
fun <T> nullsFirstComparator() = Comparator<T>{ a, b ->
when {
(a == null && b == null) -> 0
(a == null) -> -1
else -> 1
}
}
However, there are some underlying problems here.
The main one is that it's inconsistent. The general contract for a Comparator
is spelled out in the Java docs:
The implementor must ensure that
sgn(compare(x, y)) == -sgn(compare(y, x))
for all x and y
(Unfortunately the Kotlin docs don't mention any of this. It's a real shame they're not up to the standard of the Java ones.)
However, the comparator above doesn't do this; if a and b are non-null, then compare(a, b)
and compare(b, a)
are both 1!
This is likely to lead to problems; for example, depending how a sort() method is coded, it might leave the list unsorted, or never finish. Or if you use it for a sorted map, the map might fail to return some of its values, or never finish.
This can be fixed by adding a fourth case:
fun <T> nullsFirstComparator() = Comparator<T>{ a, b ->
when {
(a == null && b == null) -> 0
(a == null) -> -1
(b == null) -> 1
else -> 0
}
}
The comparator is now consistent; null values always come before non-null ones.
But it still has an undesirable feature: all non-null values are now treated as equivalent, and can't be sorted within themselves. There's no way to fix this in general, as Kotlin doesn't know how to compare the order of two arbitrary objects. But there are two ways you could tell it how.
One way is to restrict it to objects that have a natural order, i.e. which implement the Comparable
interface. (Once again, the Java docs explain this far better.)
fun <T : Comparable<T>> nullsFirstComparator() = Comparator<T>{ a, b ->
when {
(a == null && b == null) -> 0
(a == null) -> -1
(b == null) -> 1
else -> a.compareTo(b)
}
}
However, you can simplify if you use the standard library kotlin.comparisons.compareValues()
function:
fun <T : Comparable<T>> nullsFirstComparator()
= Comparator<T>{ a, b -> compareValues(a, b) }
The other is to provide an ordering yourself — which you do by providing another Comparator
to handle the non-null comparisons:
fun <T> nullsFirstComparator(comparator: Comparator<T>) = Comparator<T>{ a, b ->
when {
(a == null && b == null) -> 0
(a == null) -> -1
(b == null) -> 1
else -> c.compare(a, b)
}
}
But you don't need to write that yourself, because that's already in the Kotlin standard library, as kotlin.comparisons.nullsFirst()
!
Solution 4
There is better way to sort collections in Kotlin - you can use extension function sortedWith
like this:
list.sortedWith(Comparator { s1, s2 ->
when {
s1 == null && s2 == null -> 0
s1 == null -> -1
else -> 1
}
})
But remember, this will return copy of list.
Related videos on Youtube
Abraham George
Updated on June 04, 2022Comments
-
Abraham George almost 2 years
I'm new to kotlin, how to compare objects using
Collections
Collections.sort(list,myCustomComparator)
How can we write a
MyCustomComparator
method in kotlin?private final Comparator<CustomObject> myCustomComparator = (a, b) -> { if (a == null && b == null) { return 0; } else if (a == null) { return -1; } else if (b == null) { return 1; } };
-
omilke almost 3 years+1 for not only discussing the actual question but pointing out the basic requirements for Comparable and including Kotlin std lib.