So klonen oder kopieren Sie eine Liste in Kotlin

101

Wie kopiere ich eine Liste in Kotlin?

Ich benutze

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

Gibt es einen einfacheren Weg?

Audi
quelle
1
Ich denke, Ihre Lösung ist bereits der einfachste Weg, falls Sie kein tiefes Klonen benötigen.
Serdar Samancıoğlu

Antworten:

143

Das funktioniert gut.

val selectedSeries = series.toMutableList()
Audi
quelle
5
val selectedSeries = series.toList()funktioniert auch, weil es toMutableList()in seiner Implementierung aufruft .
Flávio Faria
4
@ FlávioFaria hat es gerade getestet ===und muss sagen toList(), kopiert die Sammlung nicht, toMutableList()tut es aber
Peppermint Paddy
3
@PeppermintPaddy Es wird kopiert, außer bei leeren Listen. Wenn die Quelle leer ist, wird Iterable.toList()zurückgegeben emptyList(), wodurch immer dasselbe (unveränderliche) Objekt zurückgegeben wird. Wenn Sie also mit testen, erhalten emptyList()Sie dasselbe Objekt zurück.
Laurence Gonsalves
51
Ich persönlich mag diese Idee nicht ... Nichts in den Dokumenten gewährt, was toMutableList()eine neue Instanz einer Liste zurückgeben sollte, wenn die Instanz, die die Methode aufruft, bereits eine veränderbare Liste ist.
BrunoJCM
4
Dies ist keine gute Antwort und definitiv nicht die richtige. Es gibt keine Garantie dafür, dass sich zukünftige Implementierungen ändern könnten, es sei denn, es ist ausdrücklich dokumentiert, dass dieser Methodenaufruf immer eine neue Kopie zurückgibt.
Bhargav
23

Sie können verwenden

Liste -> toList ()

Array -> toArray ()

ArrayList -> toArray ()

MutableList -> toMutableList ()


Beispiel:

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

Protokoll drucken:

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

Ich kann mir zwei alternative Wege einfallen lassen:

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

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

Update: Mit der neuen Type Inference Engine (Opt-In in Kotlin 1.3) können wir den generischen Typparameter im ersten Beispiel weglassen und haben Folgendes:

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

Zu Ihrer Information: Die Möglichkeit, neue Inferenz zu aktivieren, ist kotlinc -Xnew-inference ./SourceCode.ktfür die Befehlszeile oder kotlin { experimental { newInference 'enable'}für Gradle. Weitere Informationen zur neuen Typinferenz finden Sie in diesem Video: KotlinConf 2018 - Neue Typinferenz und verwandte Sprachfunktionen von Svetlana Isakova , insbesondere 'Inferenz für Builder' bei 30 '

Jacob Wu
quelle
sollte imho in 2 Antworten aufgeteilt werden, da ich denke, dass die erste richtig ist, aber der letzteren etwas Schönheit fehlt.
Holger Brandl
@ Jacob Wu: Ich war überrascht zu sehen, dass das * -Symbol in der zweiten Lösung keinen Fehler verursachte. Was tut es? Ich habe eine Suche mit "unärer Multiplikation" durchgeführt, aber nichts gefunden.
Lensflare
1
@Lensflare * bedeutet, ein Array in separate Elemente zu zerstören, z. B. mutableListOf (* [1, 2, 3]) bedeutet mutableListOf (1, 2, 3), es ist wie die entgegengesetzte Operation zu vararg
Jacob Wu
1
@ Jacob Wu: Danke. Mit Ihrer Antwort konnte ich herausfinden, dass der Operator "Spread Operator" heißt. Ich sehe, wie es hilft, indem einige Parameter mit einem Array in einer Varargs-Liste kombiniert werden. Aber welchen Nutzen hat es in Ihrem Beispiel? Ist es schneller oder so? Oder ist es der Schlüssel, um sicherzustellen, dass die Sammlung kopiert wird?
Lensflare
@Lensflare Ich denke, der Vorteil ist nur die Syntax - der Code ist kurz und es ist kein expliziter generischer Typ erforderlich (wie in meinem ersten Beispiel). Hinter den Kulissen glaube ich, dass der Code für Array-Operationen kompiliert wurde, daher sollte die Leistung gleich sein.
Jacob Wu
10

Wenn Ihre Liste eine Kotlin-Datenklasse enthält , können Sie dies tun

selectedSeries = ArrayList(series.map { it.copy() })
Levon Petrosyan
quelle
9

Sie können die bereitgestellte Erweiterung verwenden Iterable.toMutableList(), mit der Sie eine neue Liste erhalten. Leider soll, wie die Unterschrift und die Dokumentation andeuten, sichergestellt werden, dass ein a Iterableist List(genau wie toStringund viele andere to<type>Methoden). Nichts garantiert Ihnen, dass es sich um eine neue Liste handelt. Das Hinzufügen der folgenden Zeile am Anfang der Erweiterung if (this is List) return thisist beispielsweise eine legitime Leistungsverbesserung (wenn sie tatsächlich die Leistung verbessert).

Außerdem ist der resultierende Code aufgrund seines Namens nicht sehr klar.

Ich ziehe es vor, meine eigene Erweiterung hinzuzufügen, um das Ergebnis sicher zu stellen und einen viel klareren Code zu erstellen (genau wie bei Arrays ):

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

Beachten Sie, dass dies addAllder schnellste Weg zum Kopieren ist, da System.arraycopybei der Implementierung von das native verwendet wird ArrayList.

Beachten Sie auch, dass Sie dadurch nur eine flache Kopie erhalten .

Sir Codesalot
quelle
Ich mag diese Lösung. Sollte es nicht sein addAll(this@copyOf), denn thisinnen applywird auf die neu erstellte leere Liste verwiesen ? Entweder das oder mutableListOf<T>().also { it.addAll(this) }?
Franko Leon Tokalić
5

Für eine flache Kopie schlage ich vor

.map{it}

Das funktioniert für viele Sammlungstypen.

Lensflare
quelle
1
Beachten Sie, dass es für Maps nicht funktioniert . Es wird kompiliert, aber da das ita Map.Entryist und die Kopie flach ist, haben Sie die gleichen Einträge.
Noamtm
1
@noamtm ja, das meine ich mit flacher Kopie. Diese Methode kopiert niemals die Einträge. Es wird nur eine Kopie der Sammlung mit denselben Einträgen erstellt. Karte ist hier nichts Besonderes.
Lensflare
2
Mein Punkt ist, dass, obwohl es verlockend ist, es auch auf Karten zu verwenden, es kompiliert und zu funktionieren scheint - es nicht wirklich funktioniert.
Noamtm
4

Genau wie in Java:

Aufführen:

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

Karte:

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

Angenommen, Sie zielen auf die JVM (oder Android) ab. Ich bin nicht sicher, ob es für andere Ziele funktioniert, da es auf den Kopierkonstruktoren von ArrayList und HashMap basiert.

noamtm
quelle
2

Ich würde die toCollection()Erweiterungsmethode verwenden :

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

Dadurch wird ein neues MutableListElement erstellt und anschließend jedes Element des Originals zur neu erstellten Liste hinzugefügt.

Der hier abgeleitete Typ wird sein MutableList<String>. Wenn Sie die Veränderbarkeit dieser neuen Liste nicht offenlegen möchten, können Sie den Typ explizit als unveränderliche Liste deklarieren:

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

Für einfache Listen gibt es oben viele richtige Lösungen.

Es ist jedoch nur für flache Listen.

Die folgende Funktion funktioniert für jede 2-dimensionale ArrayList. ArrayListist in der Praxis gleichbedeutend mit MutableList. Interessanterweise funktioniert es nicht, wenn expliziter MutableListTyp verwendet wird. Wenn man mehr Dimensionen benötigt, muss man mehr Funktionen machen.

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
}

Demo für Integer Matrix:

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

es zeigt 12

Paulo Buchsbaum
quelle
0

Sie können den ArrayListKonstruktor verwenden:ArrayList(list)

Solomon Ucko
quelle
-1

Versuchen Sie den folgenden Code zum Kopieren der Liste in Kotlin

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