Cómo clonar o copiar una lista en kotlin

104

¿Cómo copiar la lista en Kotlin?

Estoy usando

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

¿Existe una forma más sencilla?

Audi
fuente
1
Creo que su solución ya es la forma más fácil, en caso de que no necesite una clonación profunda.
Serdar Samancıoğlu

Respuestas:

147

Esto funciona bien.

val selectedSeries = series.toMutableList()
Audi
fuente
6
val selectedSeries = series.toList()también funciona porque llama toMutableList()a su implementación.
Flávio Faria
4
@ FlávioFaria acaba de probarlo ===y tengo que decir toList()que no copia la colección, pero toMutableList()
Peppermint Paddy
3
@PeppermintPaddy Se hace copia, excepto en el caso de las listas vacías. Si la fuente está vacía, Iterable.toList()devuelve emptyList(), que siempre devuelve el mismo objeto (inmutable). Entonces, si prueba con emptyList(), obtendrá el mismo objeto.
Laurence Gonsalves
52
Personalmente, no me gusta esta idea ... Nada en la concesión de documentos que toMutableList()deba devolver una nueva instancia de una lista si la instancia que llama al método ya es una lista mutable.
BrunoJCM
4
esta no es una buena respuesta, y definitivamente no es la correcta, no hay garantía de que futuras implementaciones puedan cambiar, a menos que esté específicamente documentado que esta llamada al método siempre devolverá una nueva copia.
Bhargav
23

Puedes usar

Lista -> toList ()

Matriz -> toArray ()

ArrayList -> toArray ()

MutableList -> toMutableList ()


Ejemplo:

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

registro de impresión:

array: 4
arrayCopy: 4
array after remove: 3
arrayCopy after remove: 4
Rasoul Miri
fuente
14

Puedo pensar en dos formas alternativas:

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

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

Actualización: con el nuevo motor de inferencia de tipos (opt-in en Kotlin 1.3), podemos omitir el parámetro de tipo genérico en el primer ejemplo y tener esto:

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

Para su información, la forma de optar por la nueva Inferencia es kotlinc -Xnew-inference ./SourceCode.ktpara la línea de comandos o kotlin { experimental { newInference 'enable'}para Gradle. Para obtener más información sobre la nueva inferencia de tipos, consulte este video: KotlinConf 2018 - Nueva inferencia de tipos y características de lenguaje relacionadas de Svetlana Isakova , especialmente 'inferencia para constructores' en 30 '

Jacob Wu
fuente
debería dividirse en 2 respuestas en mi humilde opinión, ya que creo que la primera es correcta, pero la última carece de algo de belleza.
Holger Brandl
@Jacob Wu: Me sorprendió ver que el símbolo * en la segunda solución no producía un error. ¿Qué hace? Hice una búsqueda con "multiplicación unaria" pero no encontré nada.
Lensflare
1
@Lensflare * significa destruir una matriz en elementos separados, por ejemplo, mutableListOf (* [1, 2, 3]) significa mutableListOf (1, 2, 3), es como la operación opuesta a vararg
Jacob Wu
1
@Jacob Wu: Gracias. Con su respuesta, pude descubrir que el operador se llama "operador de propagación". Veo cómo ayuda combinar algunos parámetros con una matriz en una lista de varargs. Pero, ¿qué beneficio tiene en tu ejemplo? ¿Es más rápido o algo así? ¿O es la clave para asegurarse de que se copia la colección?
Lensflare
@Lensflare Creo que el beneficio es solo la sintaxis: el código es corto y no se requiere un tipo genérico explícito (como en mi primer ejemplo). Detrás de escena, creo que el código se compila para operaciones de matriz, por lo que el rendimiento debería ser el mismo.
Jacob Wu
9

Puede utilizar la extensión provista Iterable.toMutableList()que le proporcionará una nueva lista. Desafortunadamente, como sugiere su firma y documentación , está destinado a garantizar que an Iterablees un List(como toStringy muchos otros to<type>métodos). Nada te garantiza que será una nueva lista. Por ejemplo, agregar la siguiente línea al comienzo de la extensión: if (this is List) return thises una mejora legítima del desempeño (si es que realmente mejora el desempeño).

Además, debido a su nombre, el código resultante no es muy claro.

Prefiero agregar mi propia extensión para estar seguro del resultado y crear un código mucho más claro (como tenemos para las matrices ):

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

Tenga en cuenta que addAlles la forma más rápida de copiar porque usa el nativo System.arraycopyen la implementación de ArrayList.

Además, tenga en cuenta que esto solo le dará una copia superficial .

Sir Codesalot
fuente
Me gusta esta solucion ¿No debería ser así addAll(this@copyOf), porque thisdentro applyse referirá a la lista vacía recién creada? ¿O eso o mutableListOf<T>().also { it.addAll(this) }?
Franko Leon Tokalić
5

Para una copia superficial, sugiero

.map{it}

Eso funcionará para muchos tipos de colecciones.

Lensflare
fuente
1
Tenga en cuenta que no funciona para Maps. Se compila, pero como ites a Map.Entry, y la copia es poco profunda, tiene las mismas entradas.
noamtm
1
@noamtm sí, eso es lo que quiero decir con copia superficial. Este método nunca copiará las entradas. Solo hará una copia de la colección con las mismas entradas. El mapa no es nada especial aquí.
Lensflare
2
Mi punto es que, aunque también es tentador usarlo en mapas, y se compila y parece funcionar, en realidad no funciona.
noamtm
4

Como en Java:

Lista:

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

Mapa:

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

Suponiendo que está apuntando a la JVM (o Android); No estoy seguro de que funcione para otros objetivos, ya que se basa en los constructores de copia de ArrayList y HashMap.

noamtm
fuente
2

Usaría el toCollection()método de extensión :

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

Esto creará un nuevo MutableListy luego agregará cada elemento del original a la lista recién creada.

El tipo inferido aquí será MutableList<String>. Si no desea exponer la mutabilidad de esta nueva lista, puede declarar el tipo explícitamente como una lista inmutable:

val copy: List<String> = original.toCollection(mutableListOf())
Ben P.
fuente
0

Para listas simples tiene muchas soluciones correctas arriba.

Sin embargo, es solo para listas poco profundas.

La siguiente función funciona para cualquier bidimensional ArrayList. ArrayListes, en la práctica, equivalente a MutableList. Curiosamente, no funciona cuando se usa un MutableListtipo explícito . Si se necesitan más dimensiones, es necesario realizar más funciones.

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
}

Demostración para matriz de enteros:

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])

muestra 12

Paulo Buchsbaum
fuente
0

Puedes usar el ArrayListconstructor:ArrayList(list)

Salomón Ucko
fuente
-1

Pruebe el siguiente código para copiar la lista en Kotlin

arrayList2.addAll(arrayList1.filterNotNull())
Yyy
fuente