Упорядочивание
Порядок элементов — важный аспект некоторых типов коллекций. Например, два списка с одинаковыми элементами не равны, если элементы в них расположены в разном порядке.
В Kotlin порядок объектов можно определять несколькими способами.
Прежде всего, существует естественный порядок. Он определён для реализаций интерфейса Comparable. Естественный порядок используется для сортировки, если другой порядок не указан.
Большинство встроенных типов поддерживают сравнение:
Для числовых типов используется традиционный числовой порядок:
1больше, чем0;-3.4fбольше, чем-5f, и так далее.CharиStringиспользуют лексикографический порядок:bбольше, чемa;worldбольше, чемhello.
Чтобы определить естественный порядок для пользовательского типа, реализуйте в этом типе интерфейс Comparable. Для этого необходимо реализовать функцию compareTo(). Функция compareTo() должна принимать в качестве аргумента другой объект того же типа и возвращать целое число, указывающее, какой объект больше:
Положительные значения означают, что объект-получатель больше.
Отрицательные значения означают, что он меньше аргумента.
Ноль означает, что объекты равны.
Ниже приведён класс для упорядочивания версий, состоящих из основной и дополнительной частей.
class Version(val major: Int, val minor: Int): Comparable<Version> {
override fun compareTo(other: Version): Int = when {
this.major != other.major -> this.major compareTo other.major // compareTo() in the infix form
this.minor != other.minor -> this.minor compareTo other.minor
else -> 0
}
}
fun main() {
println(Version(1, 2) > Version(1, 3))
println(Version(2, 0) > Version(1, 5))
}
Пользовательские порядки позволяют сортировать экземпляры любого типа так, как вам нужно. В частности, вы можете определить порядок для объектов, не поддерживающих сравнение, или задать порядок, отличный от естественного, для типа, поддерживающего сравнение. Чтобы определить пользовательский порядок для типа, создайте для него Comparator. Интерфейс Comparator содержит функцию compare(): она принимает два экземпляра класса и возвращает целочисленный результат их сравнения. Результат интерпретируется так же, как результат функции compareTo(), описанной выше.
fun main() {
//sampleStart
val lengthComparator = Comparator { str1: String, str2: String -> str1.length - str2.length }
println(listOf("aaa", "bb", "c").sortedWith(lengthComparator))
//sampleEnd
}
Используя lengthComparator, можно упорядочить строки по длине вместо стандартного лексикографического порядка.
Более короткий способ определить Comparator — использовать функцию compareBy() из стандартной библиотеки. compareBy() принимает лямбда-функцию, которая получает значение Comparable из экземпляра, и определяет пользовательский порядок как естественный порядок полученных значений.
С помощью compareBy() компаратор длины из приведённого выше примера выглядит так:
fun main() {
//sampleStart
println(listOf("aaa", "bb", "c").sortedWith(compareBy { it.length }))
//sampleEnd
}
Также можно определить порядок по нескольким критериям. Например, чтобы сортировать строки по длине, а при одинаковой длине — по алфавиту, можно написать:
fun main() {
//sampleStart
val sortedStrings = listOf("aaa", "bb", "c", "b", "a", "aa", "ccc")
.sortedWith { a, b ->
when (val compareLengths = a.length.compareTo(b.length)) {
0 -> a.compareTo(b)
else -> compareLengths
}
}
println(sortedStrings)
// [a, b, c, aa, bb, aaa, ccc]
//sampleEnd
}
Поскольку сортировка по нескольким критериям — распространённая задача, стандартная библиотека Kotlin предоставляет функцию .thenBy(), которую можно использовать для добавления вторичного правила сортировки.
Например, можно объединить compareBy() и .thenBy(), чтобы сначала сортировать строки по длине, а затем по алфавиту, как в предыдущем примере:
fun main() {
//sampleStart
val sortedStrings = listOf("aaa", "bb", "c", "b", "a", "aa", "ccc")
.sortedWith(compareBy<String> { it.length }.thenBy { it })
println(sortedStrings)
// [a, b, c, aa, bb, aaa, ccc]
//sampleEnd
}
Пакет коллекций Kotlin предоставляет функции для сортировки коллекций в естественном, пользовательском и даже случайном порядке. На этой странице мы рассмотрим функции сортировки, применимые к неизменяемым коллекциям. Эти функции возвращают результат в виде новой коллекции, содержащей элементы исходной коллекции в нужном порядке. Сведения о функциях сортировки изменяемых коллекций на месте см. в разделе Операции сортировки списков.
Естественный порядок
Основные функции .sorted() и .sortedDescending() возвращают элементы коллекции, отсортированные по возрастанию и убыванию в соответствии с естественным порядком. Эти функции применяются к коллекциям элементов типа Comparable.
fun main() {
//sampleStart
val numbers = listOf("one", "two", "three", "four")
println("Sorted ascending: ${numbers.sorted()}")
println("Sorted descending: ${numbers.sortedDescending()}")
//sampleEnd
}
Пользовательские порядки
Для сортировки в пользовательском порядке или сортировки объектов, не поддерживающих сравнение, предназначены функции .sortedBy() и .sortedByDescending(). Они принимают функцию-селектор, преобразующую элементы коллекции в значения типа Comparable, и сортируют коллекцию в естественном порядке этих значений.
fun main() {
//sampleStart
val numbers = listOf("one", "two", "three", "four")
val sortedNumbers = numbers.sortedBy { it.length }
println("Sorted by length ascending: $sortedNumbers")
val sortedByLast = numbers.sortedByDescending { it.last() }
println("Sorted by the last letter descending: $sortedByLast")
//sampleEnd
}
Чтобы задать пользовательский порядок сортировки коллекции, можно передать собственный Comparator. Для этого вызовите функцию расширения .sortedWith(), передав ей свой Comparator. С помощью этой функции сортировка строк по длине выглядит так:
fun main() {
//sampleStart
val numbers = listOf("one", "two", "three", "four")
println("Sorted by length ascending: ${numbers.sortedWith(compareBy { it.length })}")
//sampleEnd
}
Проверка порядка сортировки
С помощью следующих функций расширения можно проверить, упорядочены ли элементы в соответствии с заданным порядком:
.isSorted().isSortedDescending().isSortedWith(comparator).isSortedBy(selector).isSortedByDescending(selector)
Эти функции расширения возвращают true, если элементы расположены в указанном порядке или если элементов меньше двух. Они возвращают false и прекращают проверку, как только находят пару элементов, расположенных не по порядку.
Для коллекций без гарантированного порядка итерации, например HashSet, результат может различаться при каждом вызове. То же относится к последовательностям, которые не выдают элементы в постоянном порядке. Чтобы получать одинаковые результаты при каждом вызове, используйте эти функции только для коллекций с гарантированным порядком итерации, например List.
При проверке значений Double и Float эти функции считают NaN больше любого другого значения, а -0.0 — меньше 0.0. Кроме того, функции .isSortedBy() и .isSortedByDescending() считают результаты селектора null меньше любого ненулевого значения.
При вызове этих функций для последовательности операция является терминальной. Она обрабатывает последовательность и возвращает значение типа Boolean вместо другой последовательности.
Вот пример проверки порядка сортировки с помощью функций .isSorted() и .isSortedBy():
data class User(val name: String, val age: Int)
fun main() {
//sampleStart
val numbers = listOf(1, 2, 3, 4)
println(numbers.isSorted())
// true
val users = listOf(
User("Alice", 24),
User("Bob", 31),
User("Charlie", 29),
)
println(users.isSortedBy(User::age))
// false
val descending = listOf(4, 3, 2, 1)
println(descending.isSortedDescending())
// true
//sampleEnd
}
Обратный порядок
Получить коллекцию в обратном порядке можно с помощью функции .reversed().
fun main() {
//sampleStart
val numbers = listOf("one", "two", "three", "four")
println(numbers.reversed())
//sampleEnd
}
Функция расширения .reversed() возвращает новую коллекцию с копиями элементов. Поэтому, если позднее изменить исходную коллекцию, это не повлияет на ранее полученные результаты .reversed().
Другая функция для изменения порядка на обратный — .asReversed()
возвращает представление той же коллекции в обратном порядке, поэтому оно может быть более лёгким и предпочтительным, чем
.reversed(), если исходный список не будет изменяться.
fun main() {
//sampleStart
val numbers = listOf("one", "two", "three", "four")
val reversedNumbers = numbers.asReversed()
println(reversedNumbers)
//sampleEnd
}
Если исходный список изменяемый, все его изменения отражаются в представлениях с обратным порядком, и наоборот.
fun main() {
//sampleStart
val numbers = mutableListOf("one", "two", "three", "four")
val reversedNumbers = numbers.asReversed()
println(reversedNumbers)
numbers.add("five")
println(reversedNumbers)
//sampleEnd
}
Однако, если неизвестно, является ли список изменяемым, или источник вообще не является списком, предпочтительнее использовать .reversed(), поскольку эта функция возвращает копию, которая впоследствии не изменится.
Случайный порядок
Наконец, существует функция .shuffled(), которая возвращает новую коллекцию List с элементами в случайном порядке. Её можно вызвать без аргументов или передать объект Random.
fun main() {
//sampleStart
val numbers = listOf("one", "two", "three", "four")
println(numbers.shuffled())
//sampleEnd
}
© 2010–2026 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/collection-ordering.html