在Kotlin中使用比较器

时间:2019-04-01 06:49:19

标签: kotlin collections comparator

我是Kotlin的新手,如何使用Collections比较对象

Collections.sort(list,myCustomComparator)

我们如何在Kotlin中编写MyCustomComparator方法?

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;
        } 
    };

5 个答案:

答案 0 :(得分:1)

这几乎可以与Java中的方法相同:

private val myCustomComparator =  Comparator<CustomObject> { a, b ->
    when {
        (a == null && b == null) -> 0
        (a == null) -> -1
        else -> 1
    }
}

if else if else ...被单个Kotlin when取代,以使代码更易读。

在Kotlin中,使用Comparator对列表进行排序也可以这样写:

val customObjects = listOf(CustomObject(), CustomObject())
customObjects.sortedWith(myCustomComparator)

答案 1 :(得分:0)

您可以将SAM conversion与lambda一起使用(因为Comparator是Java接口,而Kotlin允许您这样做)或匿名类对象。

使用lambda时,它将如下所示:

val customComparator = Comparator<CustomObject> { a, b ->
    if (a == null && b == null) {
        return 0;
    } else if (a == null) {
        return -1;
    } else if (b == null) {
        return 1;
    }
}

和匿名类版本:

val customComparator = object: Comparator<CustomObject> {
    override fun compare(a: CustomObject, b: CustomObject): Int {
        if (a == null && b == null) {
            return 0;
        } else if (a == null) {
            return -1;
        } else if (b == null) {
            return 1;
        }
    }
}

答案 2 :(得分:0)

在Kotlin中有更好的方法来对集合进行排序-您可以像这样使用扩展功能sortedWith

list.sortedWith(Comparator { s1, s2 ->
    when {
        s1 == null && s2 == null -> 0
        s1 == null -> -1
        else -> 1
    }
})

但是请记住,这将返回列表的副本

答案 3 :(得分:0)

根据其他答案,相当直接的翻译使您可以使用以下方式对列表进行排序:

fun myCustomComparator() = Comparator<CustomObject>{ a, b ->
    when {
        (a == null && b == null) -> 0
        (a == null) -> -1
        else -> 1
    }
}

现在,这里没有任何内容取决于您的CustomObject。因此,使其具有通用性很简单,因此它可以处理任何类型:

fun <T> nullsFirstComparator() = Comparator<T>{ a, b ->
    when {
        (a == null && b == null) -> 0
        (a == null) -> -1
        else -> 1
    }
}

但是,这里存在一些潜在的问题。

主要问题是不一致Java docs中详细说明了Comparator的一般合同:

  

实施者必须确保所有x和y的sgn(compare(x, y)) == -sgn(compare(y, x))

(不幸的是,Kotlin docs没有提到任何这些。真是可惜他们没有达到Java的标准。)

但是,上面的比较器没有这样做;如果a和b不为空,则compare(a, b)compare(b, a)均为1!

这可能会导致问题;例如,根据sort()方法的编码方式,它可能会使列表未排序,或者永远无法完成。或者,如果将其用于排序的地图,则该地图可能无法返回其某些值,或者永远无法完成。

这可以通过添加第四种情况来解决:

fun <T> nullsFirstComparator() = Comparator<T>{ a, b ->
    when {
        (a == null && b == null) -> 0
        (a == null) -> -1
        (b == null) -> -1
        else -> 0
    }
}

现在比较器是一致的;空值总是先于非空值。

但是它仍然具有不受欢迎的功能:所有非空值现在都被视为等效值,并且无法在其内部进行排序。由于Kotlin不知道如何比较两个任意对象的顺序,因此通常无法解决此问题。但是,您可以通过两种方式告诉它如何操作。

一种方法是将其限制为具有自然顺序的对象,即实现Comparable接口的对象。 (再一次,Java docs对此进行了更好的解释。)

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

但是,您可以使用标准库kotlin.comparisons.compareValues()函数来简化此操作:

fun <T : Comparable<T>> nullsFirstComparator()
    = Comparator<T>{ a, b -> compareValues(a, b) }

另一种方法是自己提供订单-您可以通过提供另一个 Comparator来处理非空比较:

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

但是您不需要自己编写,因为它已经在Kotlin标准库中,为kotlin.comparisons.nullsFirst()

答案 4 :(得分:0)

考虑@Alexander答案后,代码可以写为

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