Spec-Zone.ru › Kotlin 1.6

Операции со списками

List — это наиболее популярный тип встроенного набора в Kotlin. Доступ к элементам списков по индексу предоставляет мощный набор операций для списков.

Получение элементов по индексу

Списки поддерживают все распространенные операции по получению элементов: elementAt(), first(), last(), и другие, перечисленные в Получение отдельных элементов. Особенностью списков является доступ по индексу к элементам, поэтому самый простой способ чтения элемента — получение его по индексу. Это делается с помощью функции get() с индексом в качестве аргумента или сокращенного синтаксиса [index].

Если размер списка меньше указанного индекса, выбрасывается исключение. Есть две другие функции, которые помогут вам избежать таких исключений:

  • getOrElse() позволяет указать функцию для вычисления значения по умолчанию, которое должно быть возвращено, если индекс не присутствует в коллекции.

  • getOrNull() возвращает null в качестве значения по умолчанию.

fun main() {
//sampleStart
    val numbers = listOf(1, 2, 3, 4)
    println(numbers.get(0))
    println(numbers[0])
    //numbers.get(5)                         // exception!
    println(numbers.getOrNull(5))             // null
    println(numbers.getOrElse(5, {it}))        // 5
//sampleEnd
}

Получение фрагментов списка

В дополнение к общим операциям для Получения фрагментов коллекции, списки предоставляют функцию subList(), которая возвращает представление указанного диапазона элементов как список. Таким образом, если элемент исходной коллекции изменяется, он также изменяется в ранее созданных подсписках и наоборот.

fun main() {
//sampleStart
    val numbers = (0..13).toList()
    println(numbers.subList(3, 6))
//sampleEnd
}

Поиск позиций элементов

Линейный поиск

В любых списках вы можете найти позицию элемента, используя функции indexOf() и lastIndexOf(). Они возвращают первую и последнюю позицию элемента, равного заданному аргументу в списке. Если таких элементов нет, обе функции возвращают -1.

fun main() {
//sampleStart
    val numbers = listOf(1, 2, 3, 4, 2, 5)
    println(numbers.indexOf(2))
    println(numbers.lastIndexOf(2))
//sampleEnd
}

Также есть пара функций, которые принимают предикат и ищут элементы, соответствующие ему:

  • indexOfFirst() возвращает индекс первого элемента, соответствующего предикату, или -1 если таких элементов нет.

  • indexOfLast() возвращает индекс последнего элемента, соответствующего предикату, или -1 если таких элементов нет.

fun main() {
//sampleStart
    val numbers = mutableListOf(1, 2, 3, 4)
    println(numbers.indexOfFirst { it > 2})
    println(numbers.indexOfLast { it % 2 == 1})
//sampleEnd
}

Бинарный поиск в отсортированных списках

Есть еще один способ поиска элементов в списках — бинарный поиск. Он работает значительно быстрее, чем другие встроенные функции поиска, но требует, чтобы список был отсортирован по возрастанию в соответствии с определенным порядком: естественным или другим, заданным в параметре функции. В противном случае результат не определен.

Чтобы найти элемент в отсортированном списке, вызовите функцию binarySearch(), передав значение в качестве аргумента. Если такой элемент существует, функция возвращает его индекс; в противном случае она возвращает (-insertionPoint - 1), где insertionPoint — индекс, куда следует вставить этот элемент, чтобы список оставался отсортированным. Если существует более одного элемента с заданным значением, поиск может вернуть любой из их индексов.

Вы также можете указать диапазон индексов для поиска: в этом случае функция ищет только между двумя указанными индексами.

fun main() {
//sampleStart
    val numbers = mutableListOf("one", "two", "three", "four")
    numbers.sort()
    println(numbers)
    println(numbers.binarySearch("two"))  // 3
    println(numbers.binarySearch("z")) // -5
    println(numbers.binarySearch("two", 0, 2))  // -3
//sampleEnd
}

Бинарный поиск с компаратором

Когда элементы списка не Comparable, вы должны указать Comparator для использования в бинарном поиске. Список должен быть отсортирован по возрастанию в соответствии с этим Comparator. Давайте рассмотрим пример:

data class Product(val name: String, val price: Double)

fun main() {
//sampleStart
    val productList = listOf(
        Product("WebStorm", 49.0),
        Product("AppCode", 99.0),
        Product("DotTrace", 129.0),
        Product("ReSharper", 149.0))

    println(productList.binarySearch(Product("AppCode", 99.0), compareBy<Product> { it.price }.thenBy { it.name }))
//sampleEnd
}

Вот список Product экземпляров, которые не Comparable, и Comparator, определяющий порядок: продукт p1 предшествует продукту p2 если цена p1 меньше цены p2. Таким образом, имея список, отсортированный по возрастанию в соответствии с этим порядком, мы используем binarySearch() для нахождения индекса указанного Product.

Пользовательские компараторы также полезны, когда список использует порядок, отличный от естественного, например, регистронезависимый порядок для String элементов.

fun main() {
//sampleStart
    val colors = listOf("Blue", "green", "ORANGE", "Red", "yellow")
    println(colors.binarySearch("RED", String.CASE_INSENSITIVE_ORDER)) // 3
//sampleEnd
}

Бинарный поиск по функции сравнения

Бинарный поиск с функцией сравнения позволяет находить элементы без явного указания значений поиска. Вместо этого он принимает функцию сравнения, отображающую элементы в Int значения и ищет элемент, где функция возвращает ноль. Список должен быть отсортирован по возрастанию в соответствии с предоставленной функцией; другими словами, возвращаемые значения сравнения должны возрастать от одного элемента списка к следующему.

import kotlin.math.sign
//sampleStart
data class Product(val name: String, val price: Double)

fun priceComparison(product: Product, price: Double) = sign(product.price - price).toInt()

fun main() {
    val productList = listOf(
        Product("WebStorm", 49.0),
        Product("AppCode", 99.0),
        Product("DotTrace", 129.0),
        Product("ReSharper", 149.0))

    println(productList.binarySearch { priceComparison(it, 99.0) })
}
//sampleEnd

Оба вида бинарного поиска с компаратором и функцией сравнения могут быть выполнены и для диапазонов списков.

Операции записи в списки

В дополнение к операциям изменения коллекций, описанным в Операциях записи в коллекции, изменяемые списки поддерживают специфические операции записи. Такие операции используют индекс для доступа к элементам, расширяя возможности изменения списка.

Добавление

Для добавления элементов в определённую позицию в списке используйте add() и addAll(), передавая позицию вставки элемента в качестве дополнительного аргумента. Все элементы, следующие за указанной позицией, сдвигаются вправо.

fun main() {
//sampleStart
    val numbers = mutableListOf("one", "five", "six")
    numbers.add(1, "two")
    numbers.addAll(2, listOf("three", "four"))
    println(numbers)
//sampleEnd
}

Обновление

Списки также предлагают функцию замены элемента в заданной позиции - set() и её операторную форму []. set() не изменяет индексы других элементов.

fun main() {
//sampleStart
    val numbers = mutableListOf("one", "five", "three")
    numbers[1] =  "two"
    println(numbers)
//sampleEnd
}

fill() просто заменяет все элементы коллекции указанным значением.

fun main() {
//sampleStart
    val numbers = mutableListOf(1, 2, 3, 4)
    numbers.fill(3)
    println(numbers)
//sampleEnd
}

Удаление

Для удаления элемента по заданной позиции из списка используйте функцию removeAt(), передав позицию в качестве аргумента. Все индексы элементов, следующих за удалённым элементом, уменьшатся на единицу.

fun main() {
//sampleStart
    val numbers = mutableListOf(1, 2, 3, 4, 3)    
    numbers.removeAt(1)
    println(numbers)
//sampleEnd
}

Сортировка

В разделе Порядок элементов коллекции мы описываем операции, которые извлекают элементы коллекции в определённом порядке. Для изменяемых списков стандартная библиотека предлагает аналогичные расширяющие функции, которые выполняют те же операции упорядочения на месте. При применении такой операции к экземпляру списка порядок элементов в этом конкретном экземпляре изменяется.

Функции сортировки на месте имеют сходные имена с функциями, применяемыми к неизменяемым спискам, но без суффикса ed/d:

  • sort* вместо sorted* в названиях всех функций сортировки: sort(), sortDescending(), sortBy() и т.д.

  • shuffle() вместо shuffled().

  • reverse() вместо reversed().

asReversed(), вызываемая для изменяемого списка, возвращает другой изменяемый список, который является обратным представлением исходного списка. Изменения в этом представлении отражаются в исходном списке. Приведённый ниже пример демонстрирует функции сортировки для изменяемых списков:

fun main() {
//sampleStart
    val numbers = mutableListOf("one", "two", "three", "four")

    numbers.sort()
    println("Sort into ascending: $numbers")
    numbers.sortDescending()
    println("Sort into descending: $numbers")

    numbers.sortBy { it.length }
    println("Sort into ascending by length: $numbers")
    numbers.sortByDescending { it.last() }
    println("Sort into descending by the last letter: $numbers")
    
    numbers.sortWith(compareBy<String> { it.length }.thenBy { it })
    println("Sort by Comparator: $numbers")

    numbers.shuffle()
    println("Shuffle: $numbers")

    numbers.reverse()
    println("Reverse: $numbers")
//sampleEnd
}
Последнее изменение: 07 апреля 2022
Операции записи в коллекции Операции для множеств

© 2010–2022 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/list-operations.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API