Использование компаратора в котлине - PullRequest
0 голосов
/ 01 апреля 2019

Я новичок в kotlin, как сравнивать объекты, используя Collections

Collections.sort(list,myCustomComparator)

Как мы можем написать 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 ]

2 голосов
/ 01 апреля 2019

Это можно сделать почти так же, как в 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)
0 голосов
/ 01 апреля 2019

После рассмотрения ответа @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
        }
    }
0 голосов
/ 01 апреля 2019

Как и в других ответах, достаточно прямой перевод позволяет сортировать список, например, с помощью:

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

Однако здесь есть некоторые основные проблемы.

Основным является то, что это противоречиво . Генеральный контракт на Comparator прописан в Документах Java :

Разработчик должен убедиться, что sgn(compare(x, y)) == -sgn(compare(y, x)) для всех x и y

(К сожалению, Kotlin документы не упоминают ничего из этого. Жаль, что они не соответствуют стандартам Java.)

Однако приведенный выше компаратор этого не делает; если a и b не равны NULL, тогда 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
    }
}

Компаратор теперь согласован; нулевые значения всегда предшествуют ненулевым.

Но это все еще имеет нежелательную особенность: все ненулевые значения теперь обрабатываются как эквивалентные и не могут быть отсортированы внутри самих себя. В общем, исправить это невозможно, поскольку Котлин не знает, как сравнить порядок двух произвольных объектов. Но есть два способа узнать, как это сделать.

Один из способов - ограничить его объектами, имеющими естественный порядок, т. Е. Реализующими интерфейс Comparable. (Еще раз, Java-документы объясняют это гораздо лучше.)

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()!

0 голосов
/ 01 апреля 2019

Существует лучший способ сортировки коллекций в Kotlin - вы можете использовать функцию расширения sortedWith следующим образом:

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

Но помните, это вернет copy списка.

0 голосов
/ 01 апреля 2019

Вы можете использовать либо SAM преобразование с лямбда-выражением (потому что Comparator - это интерфейс Java, и Kotlin позволит вам сделать это), либо анонимный объект класса.

С лямбдой это будет выглядеть так:

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