Как клонировать или скопировать список в котлине


105

Как скопировать список в Котлине?

я использую

val selectedSeries = mutableListOf<String>()
selectedSeries.addAll(series)

Есть способ попроще?


1
Думаю, ваше решение - это уже самый простой способ, если вам не нужно глубокое клонирование.
Сердар Саманджиоглу

Ответы:


147

Это прекрасно работает.

val selectedSeries = series.toMutableList()

6
val selectedSeries = series.toList()также работает, потому что требует toMutableList()своей реализации.
Flávio Faria

4
@ FlávioFaria просто протестировали его с ===и должен сказать , toList()не копировать коллекции, но toMutableList()делает
Peppermint Пэдди

3
@PeppermintPaddy Это делает копию, за исключением случая пустых списков. Если источник пуст, Iterable.toList()возвращается emptyList(), который всегда возвращает один и тот же (неизменяемый) объект. Так что если вы попробуете, emptyList()вы получите тот же объект обратно.
Лоуренс Гонсалвес

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

@BrunoJCM, это уже не так. В документации Kotlin указано, что toMutableList()возвращает новый список: «Возвращает новый MutableList, заполненный всеми элементами этой коллекции».
Пяр Нильс Амсен,

23

Ты можешь использовать

Список -> toList ()

Массив -> toArray ()

ArrayList -> toArray ()

MutableList -> toMutableList ()


Пример:

val array = arrayListOf("1", "2", "3", "4")

val arrayCopy = array.toArray() // copy array to other array

Log.i("---> array " ,  array?.count().toString())
Log.i("---> arrayCopy " ,  arrayCopy?.count().toString())

array.removeAt(0) // remove first item in array 

Log.i("---> array after remove" ,  array?.count().toString())
Log.i("---> arrayCopy after remove" ,  arrayCopy?.count().toString())

распечатать журнал:

array: 4
arrayCopy: 4
array after remove: 3
arrayCopy after remove: 4

15

Я могу предложить два альтернативных способа:

1. val selectedSeries = mutableListOf<String>().apply { addAll(series) }

2. val selectedSeries = mutableListOf(*series.toTypedArray())

Обновление: с новым механизмом вывода типа (опция в Kotlin 1.3) мы можем опустить параметр общего типа в 1-м примере и получить следующее:

1. val selectedSeries = mutableListOf().apply { addAll(series) }

FYI. Способ включения нового вывода - kotlinc -Xnew-inference ./SourceCode.ktдля командной строки или kotlin { experimental { newInference 'enable'}для Gradle. Для получения дополнительной информации о новом выводе типов посмотрите это видео: KotlinConf 2018 - Новый вывод типов и связанные с ним языковые функции от Светланы Исаковой , особенно «вывод для строителей» на 30-й странице.


должен быть разделен на 2 ответа imho, так как я думаю, что первый правильный, но последнему не хватает красоты.
Хольгер Брандл

@Jacob Wu: Я был удивлен, увидев, что символ * во втором решении не вызывает ошибки. Что оно делает? Я сделал поиск с «унарным умножением», но ничего не нашел.
Lensflare

1
@Lensflare * означает разделение массива на отдельные элементы, например mutableListOf (* [1, 2, 3]) означает mutableListOf (1, 2, 3), это похоже на операцию, противоположную vararg
Джейкоб Ву

1
@ Джейкоб Ву: Спасибо. С вашим ответом я смог узнать, что этот оператор называется «оператор распространения». Я вижу, как это помогает, объединив некоторые параметры с массивом в список varargs. Но какая польза от этого в вашем примере? Это быстрее что ли? Или это ключ к копированию коллекции?
Lensflare

@Lensflare Я думаю, что преимущество заключается только в синтаксисе - код короткий, и явного общего типа не требуется (как в моем 1-м примере). Я считаю, что за кулисами код скомпилирован для операций с массивами, поэтому производительность должна быть такой же.
Джейкоб Ву


9

Вы можете использовать предоставленное расширение, Iterable.toMutableList()которое предоставит вам новый список. К сожалению, как следует из его подписи и документации , он предназначен для обеспечения того, чтобы объект Iterableбыл List(как toStringи многие другие to<type>методы). Ничто не гарантирует вам, что это будет новый список. Например, добавив следующую строку в начале расширения:if (this is List) return this является законным улучшением производительности (если оно действительно улучшает производительность).

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

Я предпочитаю добавить собственное расширение, чтобы быть уверенным в результате и создать гораздо более понятный код (как у нас для массивов ):

fun <T> List<T>.copyOf(): List<T> {
    val original = this
    return mutableListOf<T>().apply { addAll(original) }
}

fun <T> List<T>.mutableCopyOf(): MutableList<T> {
    val original = this
    return mutableListOf<T>().apply { addAll(original) }
}

Обратите внимание, что addAllэто самый быстрый способ копирования, поскольку он использует собственный System.arraycopyв реализации ArrayList.

Также имейте в виду, что это даст вам только поверхностную копию .


Мне нравится это решение. Разве не должно быть addAll(this@copyOf), потому что thisвнутри applyбудет ссылаться на только что созданный пустой список? Либо так, либо mutableListOf<T>().also { it.addAll(this) }?
Франко Леон Токалич,

5

Для мелкой копии я предлагаю

.map{it}

Это будет работать для многих типов коллекций.


1
Обратите внимание, что это не работает для Maps. Он компилируется, но, поскольку itэто a Map.Entry, а копия неглубокая, у вас есть те же записи.
noamtm

1
@noamtm да, это то, что я имею в виду под мелкой копией. Этот метод никогда не скопирует записи. Будет создана только копия коллекции с такими же записями. Карта здесь ничего особенного.
Lensflare

2
Я хочу сказать, что хотя заманчиво использовать его и на картах, он компилируется и, кажется, работает, на самом деле это не работает.
noamtm

4

Как и в Java:

Список:

    val list = mutableListOf("a", "b", "c")
    val list2 = ArrayList(list)

Карта:

    val map = mutableMapOf("a" to 1, "b" to 2, "c" to 3)
    val map2 = HashMap(map)

Предполагая, что вы ориентируетесь на JVM (или Android); Я не уверен, что он работает для других целей, поскольку он полагается на конструкторы копирования ArrayList и HashMap.


2

Я хотел бы использовать в toCollection()метод расширения :

val original = listOf("A", "B", "C")
val copy = original.toCollection(mutableListOf())

Это создаст новый, MutableListа затем добавит каждый элемент оригинала во вновь созданный список.

Предполагаемый тип здесь будет MutableList<String>. Если вы не хотите раскрывать изменчивость этого нового списка, вы можете явно объявить тип как неизменяемый список:

val copy: List<String> = original.toCollection(mutableListOf())

0

Для простых списков есть много правильных решений выше.

Однако это только для мелководных списков.

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

fun <T>cloneMatrix(v:ArrayList<ArrayList<T>>):ArrayList<ArrayList<T>>{
  var MatrResult = ArrayList<ArrayList<T>>()
  for (i in v.indices) MatrResult.add(v[i].clone() as ArrayList<T>)
  return MatrResult
}

Демо для целочисленной матрицы:

var mat = arrayListOf(arrayListOf<Int>(1,2),arrayListOf<Int>(3,12))
var mat2 = ArrayList<ArrayList<Int>>()
mat2 = cloneMatrix<Int>(mat)
mat2[1][1]=5
println(mat[1][1])

это показывает 12



-1

Попробуйте ниже код для копирования списка в Котлине

arrayList2.addAll(arrayList1.filterNotNull())
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.