Comment cloner ou copier une liste dans Kotlin

105

Comment copier la liste dans Kotlin?

j'utilise

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

Y a-t-il un moyen plus simple?

Audi
la source
1
Je pense que votre solution est déjà le moyen le plus simple, au cas où vous n'auriez pas besoin d'un clonage profond.
Serdar Samancıoğlu

Réponses:

147

Cela fonctionne très bien.

val selectedSeries = series.toMutableList()
Audi
la source
6
val selectedSeries = series.toList()fonctionne aussi car il appelle toMutableList()dans sa mise en œuvre.
Flávio Faria
4
@ FlávioFaria vient de le tester avec ===et je dois dire toList()qu'il ne copie pas la collection, mais le toMutableList()fait
Peppermint Paddy
3
@PeppermintPaddy Il ne copie, sauf dans le cas des listes vides. Si la source est vide, Iterable.toList()renvoie emptyList(), qui renvoie toujours le même objet (immuable). Donc, si vous testez avec, emptyList()vous récupérerez le même objet.
Laurence Gonsalves
4
ce n'est pas une bonne réponse, et certainement pas la bonne, il n'y a aucune garantie que les futures implémentations pourraient changer, à moins qu'il ne soit spécifiquement documenté que cet appel de méthode retournera toujours une nouvelle copie.
Bhargav
@BrunoJCM, ce n'est plus le cas. L'état de la documentation Kotlin qui toMutableList()renvoie une nouvelle liste, "Renvoie une nouvelle MutableList remplie de tous les éléments de cette collection.".
Pär Nils Amsen le
23

Vous pouvez utiliser

Liste -> toList ()

Array -> toArray ()

ArrayList -> toArray ()

MutableList -> toMutableList ()


Exemple:

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

journal d'impression:

array: 4
arrayCopy: 4
array after remove: 3
arrayCopy after remove: 4
Rasoul Miri
la source
15

Je peux proposer deux méthodes alternatives:

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

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

Mise à jour: avec le nouveau moteur d'inférence de type (opt-in dans Kotlin 1.3), nous pouvons omettre le paramètre de type générique dans le 1er exemple et avoir ceci:

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

Pour votre information, la façon d'accepter une nouvelle inférence est kotlinc -Xnew-inference ./SourceCode.ktpour la ligne de commande ou kotlin { experimental { newInference 'enable'}pour Gradle. Pour plus d'informations sur la nouvelle Inférence de Type, regardez cette vidéo: KotlinConf 2018 - Nouvelle Inférence de Type et Fonctionnalités de Langage Associées par Svetlana Isakova , en particulier 'inférence pour les constructeurs' à 30 '

Jacob Wu
la source
devrait être divisé en 2 réponses à mon humble avis, car je pense que la première est correcte, mais la dernière manque de beauté.
Holger Brandl
@Jacob Wu: J'ai été surpris de voir que le symbole * dans la deuxième solution ne produisait pas d'erreur. Qu'est ce que ça fait? J'ai fait une recherche avec "multiplication unaire" mais je n'ai rien trouvé.
Lensflare
1
@Lensflare * signifie détruire un tableau en éléments séparés, par exemple mutableListOf (* [1, 2, 3]) signifie mutableListOf (1, 2, 3), c'est comme l'opération opposée à vararg
Jacob Wu
1
@Jacob Wu: Merci. Avec votre réponse, j'ai pu découvrir que l'opérateur s'appelle «opérateur de diffusion». Je vois comment cela aide en combinant certains paramètres avec un tableau dans une liste de varargs. Mais quel avantage cela a-t-il dans votre exemple? Est-ce plus rapide ou quelque chose? Ou est-ce la clé pour s'assurer que la collection est copiée?
Lensflare
@Lensflare Je pense que l'avantage est juste la syntaxe - le code est court et aucun type générique explicite n'est requis (comme dans mon premier exemple). En coulisse, je crois que le code est compilé pour des opérations de tableau, donc les performances devraient être les mêmes.
Jacob Wu
9

Vous pouvez utiliser l'extension fournie Iterable.toMutableList()qui vous fournira une nouvelle liste. Malheureusement, comme le suggèrent sa signature et sa documentation , il est destiné à garantir que an Iterableest un List(comme toStringet de nombreuses autres to<type>méthodes). Rien ne vous garantit que ce sera une nouvelle liste. Par exemple, ajouter la ligne suivante au début de l'extension: if (this is List) return thisest une amélioration légitime des performances (si elle améliore effectivement les performances).

De plus, à cause de son nom, le code résultant n'est pas très clair.

Je préfère ajouter ma propre extension pour être sûr du résultat et créer un code beaucoup plus clair (comme nous l'avons fait pour les tableaux ):

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

Notez que addAllc'est le moyen le plus rapide de copier car il utilise le natif System.arraycopydans l'implémentation de ArrayList.

Aussi, sachez que cela ne vous donnera qu'une copie superficielle .

Sir Codesalot
la source
J'aime cette solution. Cela ne devrait-il pas être le cas addAll(this@copyOf), car thisinside applyfera référence à la liste vide nouvellement créée? Soit ça ou mutableListOf<T>().also { it.addAll(this) }?
Franko Leon Tokalić le
5

Pour une copie superficielle, je suggère

.map{it}

Cela fonctionnera pour de nombreux types de collections.

Reflet
la source
1
Notez que cela ne fonctionne pas pour Maps. Il compile, mais puisque le itest un Map.Entry, et que la copie est superficielle, vous avez les mêmes entrées.
noamtm
1
@noamtm oui, c'est ce que je veux dire par copie superficielle. Cette méthode ne copiera jamais les entrées. Il ne fera qu'une copie de la collection avec les mêmes entrées. La carte n'a rien de spécial ici.
Lensflare
2
Ce que je veux dire, c'est que même s'il est tentant de l'utiliser aussi sur des cartes, et qu'il se compile et semble fonctionner, cela ne fonctionne pas vraiment.
noamtm
4

Tout comme en Java:

Liste:

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

Carte:

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

En supposant que vous ciblez la JVM (ou Android); Je ne suis pas sûr que cela fonctionne pour d'autres cibles, car il repose sur les constructeurs de copie d'ArrayList et HashMap.

noamtm
la source
2

J'utiliserais la toCollection()méthode d'extension :

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

Cela créera un nouveau MutableList, puis ajoutera chaque élément de l'original à la liste nouvellement créée.

Le type déduit ici sera MutableList<String>. Si vous ne souhaitez pas exposer la mutabilité de cette nouvelle liste, vous pouvez déclarer le type explicitement comme une liste immuable:

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

Pour les listes simples a de nombreuses bonnes solutions ci-dessus.

Cependant, c'est juste pour les listes peu profondes.

La fonction ci-dessous fonctionne pour n'importe quelle dimension 2 ArrayList. ArrayListest, en pratique, équivalent à MutableList. Fait intéressant, cela ne fonctionne pas lors de l'utilisation de MutableListtype explicite . Si l'on a besoin de plus de dimensions, il faut faire plus de fonctions.

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
}

Démo pour la matrice entière:

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

ça montre 12

Paulo Buchsbaum
la source
0

Vous pouvez utiliser le ArrayListconstructeur:ArrayList(list)

Solomon Ucko
la source
-1

Essayez ci-dessous le code pour copier la liste à Kotlin

arrayList2.addAll(arrayList1.filterNotNull())
Yyy
la source