How to clone or copy a list in kotlin
Asked Answered
V

15

241

How to copy list in Kotlin?

I'm using

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

Is there a easier way?

Virgievirgil answered 20/10, 2017 at 9:19 Comment(2)
I think your solution is already the easiest way, in case you don't need deep cloning.Calamanco
Copying a list just copies references to items. Items themselves won't clone. Don't forget to clone items during copying lists if you wish to have deep cloned lists.Downwash
V
286

This works fine.

val selectedSeries = series.toMutableList()
Virgievirgil answered 20/10, 2017 at 9:22 Comment(8)
val selectedSeries = series.toList() also works because it calls toMutableList() in its implementation.Languor
@FlávioFaria just tested it with === and have to say toList() doesn't copy the collection, but toMutableList() doesWalkin
@PeppermintPaddy It does copy, except in the case of empty lists. If the source is empty, Iterable.toList() returnsemptyList(), which always returns the same (immutable) object. So if you test with emptyList() you'll get the same object back.Bakki
this is not a good answer, and definitely not the right one, there is no guarantee that future implementations might change, unless its specifically documented that this method call will always return a new copy.Connaught
@BrunoJCM, that's not the case anymore. The Kotlin docs state that toMutableList() returns a new list, "Returns a new MutableList filled with all elements of this collection.".Heavyset
@PärNilsAmsen Verified what you stated is valid! Once contents changed, the two mutablelist will have a different hash. play.kotlinlang.org/…Audiometer
This help also when you face the problem of overwriting some data class (List) item, just before to reassign the list make sure you're assigning a MutableList.Inwrought
It’s still the least intuitive way ever if you want an immutable snapshot of a mutable collection to call something like .toMutable… on it.Maleate
W
55

You can use

List -> toList()

Array -> toArray()

ArrayList -> toArray()

MutableList -> toMutableList()


Example:

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

print log:

array: 4
arrayCopy: 4
array after remove: 3
arrayCopy after remove: 4
Watts answered 20/10, 2018 at 16:54 Comment(1)
You're confusing Array with ArrayList. They're two very different things. In your code array is actually an ArrayList while arrayCopy is an Array. Their names are misleading.Selfaggrandizement
A
52

If your list is holding kotlin data class, you can do this

selectedSeries = ArrayList(series.map { it.copy() })
Acreinch answered 1/3, 2019 at 5:42 Comment(6)
what if you want to copy only 1 attribute of the arraylist to another arraylist?Merta
best response. Thx. All the others not workingIce
If you want a mutableList, you can use selectedSeries = series.map { it.copy() }.toMutableList()Kela
Thanks a lot. This is the right answer when working with lists of custom models or data class.Foxtrot
this is the best answer for me since we're using delegate adapter recyclerViewBegotten
This gives me some ideas to deal with my case, thanks!Kensell
D
22

I can come up with two alternative ways:

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

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

Update: with the new Type Inference engine(opt-in in Kotlin 1.3), We can omit the generic type parameter in 1st example and have this:

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

FYI.The way to opt-in new Inference is kotlinc -Xnew-inference ./SourceCode.kt for command line, or kotlin { experimental { newInference 'enable'} for Gradle. For more info about the new Type Inference, check this video: KotlinConf 2018 - New Type Inference and Related Language Features by Svetlana Isakova, especially 'inference for builders' at 30'

Donia answered 9/7, 2018 at 2:45 Comment(7)
should be splitted into 2 answers imho, since I think the first one is correct, but the latter lacks some beauty.Tidy
@Jacob Wu: I was surprised to see that the * symbol in the second solution did not produce an error. What does it do? I did a search with "unary multiplication" but didn't find anything.Oblation
@Oblation * means to destruct an array into separate items, e.g. mutableListOf( * [1, 2, 3] ) means mutableListOf(1, 2, 3), it's like the opposite operation to varargDonia
@Jacob Wu: Thank you. With your answer, I was able to find out that the operator is called "spread operator". I see how it helps by combining some parameters with an array into a varargs list. But what benefit does it have in your example? Is it faster or something? Or is it the key to ensure that the collection is copied?Oblation
@Oblation I think the benefit is just the syntax - the code is short, and no explicit generic type is required(like in my 1st example). Behind the scene, I believe the code is compiled to array operations, so performance should be the same.Donia
it's inefficient. The same as Java's List.addAll #54381827 Use https://mcmap.net/q/116576/-how-to-clone-or-copy-a-list-in-kotlin Test it try.kotlinlang.org/#/UserProjects/pllbim18v1vfa6one4i8l0hasj/…Tousle
Be careful about copying lists of non-primitive objects this way. The new list will contain the same objects as the old list, so changing an item in one will affect it in both lists. A true deep copy will also copy the list items. See https://mcmap.net/q/119280/-deep-copy-of-list-with-objects-in-kotlin/1015595.Sleuth
F
21

Just like in Java:

List:

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

Map:

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

Assuming you're targeting the JVM (or Android); I'm not sure it works for other targets, as it relies on the copy constructors of ArrayList and HashMap.

Flieger answered 29/8, 2019 at 11:30 Comment(0)
F
19

You can use the provided extension Iterable.toMutableList() which will provide you with a new list. Unfortunately, as its signature and documentation suggest, it's meant to ensure that an Iterable is a List (just like toString and many other to<type> methods). Nothing guarantees you that it's going to be a new list. For instance, adding the following line at the beginning of the extension: if (this is List) return this is a legitimate performance improvement (if it indeed improves the performance).

Also, because of its name, the resulting code isn't very clear.

I prefer to add my own extension to be sure of the result and create a much more clear code (just like we have for arrays):

fun <T> List<T>.copyOf(): List<T> {
    return mutableListOf<T>().also { it.addAll(this) }
}

fun <T> List<T>.mutableCopyOf(): MutableList<T> {
    return mutableListOf<T>().also { it.addAll(this) }
}

Note that addAll is the fastest way to copy because it uses the native System.arraycopy in the implementation of ArrayList.

Also, beware that this will only give you a shallow copy.

EDIT:

You might want to use the more generic version:

fun <T> Collection<T>.copyOf(): Collection<T> {
    return mutableListOf<T>().also { it.addAll(this) }
}

fun <T> Collection<T>.mutableCopyOf(): MutableCollection<T> {
    return mutableListOf<T>().also { it.addAll(this) }
}
Flowerer answered 11/12, 2019 at 14:44 Comment(1)
I like this solution. Shouldn't it be addAll(this@copyOf), because this inside apply will refer to the newly created empty list? Either that or mutableListOf<T>().also { it.addAll(this) }?Accouterment
O
9

For a shallow copy, I suggest

.map{it}

That will work for many collection types.

Oblation answered 24/10, 2018 at 16:35 Comment(3)
Note that it doesn't work for Maps. It compiles, but since the it is a Map.Entry, and the copy is shallow, you have the same entries.Flieger
@Flieger yes, that is what I mean with shallow copy. This method will never copy the entries. It will only make a copy of the collection with the same entries. Map is nothing special here.Oblation
My point is, that even though it's tempting to use it on maps too, and it compiles and seems to work - it doesn't really work.Flieger
A
8

You can use the ArrayList constructor: ArrayList(list)

Anderton answered 17/5, 2020 at 4:3 Comment(0)
B
5
var oldList: List<ClassA>?
val newList = oldList.map { it.copy() }
Boutin answered 4/11, 2021 at 12:6 Comment(0)
R
3

I would use the toCollection() extension method:

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

This will create a new MutableList and then add each element of the original to the newly-created list.

The inferred type here will be MutableList<String>. If you don't want to expose the mutability of this new list, you can declare the type explicitly as an immutable list:

val copy: List<String> = original.toCollection(mutableListOf())
Renal answered 26/7, 2019 at 19:43 Comment(0)
A
3
val selectedSeries = listOf(*series.toTypedArray())
Ashbaugh answered 29/1, 2021 at 6:11 Comment(0)
G
2

IMHO the best and most idomatic way is use collection builders in new versions of Kotlin (1.6+)

   val shallowListCopy = buildList { addAll(list) }
Gunyah answered 12/1, 2023 at 12:58 Comment(6)
Why did you call it a shallow copy? Overview of the function and test of its behaviour shows it returns a deep copy -- not shallow.Latashalatashia
@Latashalatashia I meant it form point of view on items... surely the list is a different instance, but it still referencing elements of the original list.Gunyah
you are right, it is indeed a shallow copy. I wrote a test suite to confirm its behaviour. From your perspective what would you suggest as a deep copy in the modern kotlin?Latashalatashia
The best and simplest way is to have models as a structure of data classes, and you can copy them without additional cost, like maintain your own copy methods.Gunyah
listOf(instances).map{ it.copy() }Gunyah
Thanks, I came to this idea too, but I also got information in case you will have Collection as a member field of your data class, .copy() call would not make a deep copy of this collection. Haven't verified it yet.Latashalatashia
L
1

After trying shallow copy, deep copy cloning and many more i found this solution surely it will work for you.

val iterator: Iterator<Object> = yourList.iterator()
        while (iterator.hasNext())
        newList.add(iterator.next().copy())
Lylalyle answered 14/6, 2021 at 10:18 Comment(0)
R
0

For simple lists has many right solutions above.

However, it's just for shallows lists.

The below function works for any 2 dimensional ArrayList. ArrayList is, in practice, equivalent to MutableList. Interestingly it doesn't work when using explicit MutableList type. If one needs more dimensions, it's necessary make more functions.

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

it shows 12

Rodolforodolph answered 5/9, 2019 at 14:40 Comment(0)
C
-6

Try below code for copying list in Kotlin

arrayList2.addAll(arrayList1.filterNotNull())
Caen answered 28/10, 2019 at 9:35 Comment(1)
This doesn't copy a list, it just adds a list to an exiting list (so the result would be ArrayList<ArrayList<T>>).Oneway

© 2022 - 2024 — McMap. All rights reserved.