Идиоматическим способом в kotlin преобразовать / добавить в список? - PullRequest
1 голос
/ 25 июня 2019

Скажем, я пытаюсь реализовать какую-то функцию:

private fun List<Int>.padWithIndices(newLength: Int): List<Int>

Он принимает список и возвращает список с некоторыми добавленными значениями, где для каждого нового значения это [i] = i.

Kotlin дает нам множество хороших способов добавления в списки, объединения двух разных списков, создания изменяемых списков и т. Д. Так много, что я не знаю, что лучше в этом сценарии, и я не знаю, если Есть какие-то скрытые подводные камни. Это мое оригинальное решение на основе цикла, существуют ли более эффективные / идиоматические решения?

fun List<Int>.padWithIndices(newLength: Int): List<Int> {
    var newList = this
    for (x in size until newLength) {
        newList += x
    }
    return newList
}

Ответы [ 3 ]

4 голосов
/ 25 июня 2019

Хотя ваше решение вполне читабельно, оно копирует все элементы оригинального List<Int> на каждой итерации for -loop, так как += на var newList: List<Int> будет каждый раз создавать новый список.Вычислительная сложность наихудшего случая - O (n²), которую можно улучшить до O (n).


Для этого конкретного случая, когда вы просто хотите добавить непрерывный диапазон в список,Однострочная функция, подобная этой, будет работать:

fun List<Int>.padWithIndices(newLength: Int): List<Int> = 
    plus(size until newLength)

(исполняемый образец)

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

Вы также можете вызвать plus как оператор +, заменив строку наthis + (size until newLength).


Для случаев использования с более сложной логикой для вычисления элементов, которые вы добавляете в список, вы можете использовать List(n) { ... } фабричная функция , которая принимаетЛямбда обеспечивает элементы списка и таким образом гарантирует, что каждый элемент списка вычисляется только один раз:

fun List<Int>.padWithIndices(newLength: Int): List<Int> = 
    List(newLength) { index -> getOrNull(index) ?: index }

(исполняемый образец)

ФункцияgetOrNull(index) возвращает элемент по указанному индексу или null, если индекс выходит за пределы.Последний обрабатывается ?: index.


В качестве альтернативы, переписать ваше решение и использовать изменяемый список внутри реализации функции, добавляя элементы к нему, а затем возвращать его только для чтения List<Int>:

fun List<Int>.padWithIndices(newLength: Int): List<Int> =
    toMutableList().apply {
        addAll(size until newLength)
    }

(готовый образец)

0 голосов
/ 25 июня 2019

Однострочное:

fun List<Int>.padWithIndices(newLength: Int): List<Int> =
  toMutableList().apply { addAll(size until newLength) }

Преобразование исходного списка в MutableList<Int> (при поддержке ArrayList в JVM), затем создание IntRange от размера скопированного списка до newLength, вызов MutableList<Int>.addAll(Iterable<Int>)и в конечном итоге снижается до List<Int>.

0 голосов
/ 25 июня 2019

Это работает, но вы добавляете в список в цикле, что может вызвать перераспределение в списке.

Это более эффективно, поскольку список сначала инициализируется с правильным размером:

fun List<Int>.padWithIndices(newLength: Int): List<Int> = List(newLength) { index ->
    if (index < size) {
        get(index)
    } else {
        index
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...