Jak sklonować lub skopiować listę w Kotlin

104

Jak skopiować listę w Kotlinie?

używam

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

Czy jest łatwiejszy sposób?

Audi
źródło
1
Myślę, że twoje rozwiązanie jest już najłatwiejsze, na wypadek, gdybyś nie potrzebował głębokiego klonowania.
Serdar Samancıoğlu

Odpowiedzi:

147

To działa dobrze.

val selectedSeries = series.toMutableList()
Audi
źródło
6
val selectedSeries = series.toList()działa również, ponieważ wzywa toMutableList()do jego wykonania.
Flávio Faria
4
@ FlávioFaria właśnie go przetestował ===i muszę powiedzieć, toList()że nie kopiuje kolekcji, ale toMutableList()robi
Peppermint Paddy
3
@PeppermintPaddy To robi kopię, z wyjątkiem przypadku pustych list. Jeśli źródło jest puste, Iterable.toList()zwraca emptyList(), która zawsze zwraca ten sam (niezmienny) obiekt. Więc jeśli przeprowadzisz test emptyList(), otrzymasz z powrotem ten sam obiekt.
Laurence Gonsalves
52
Osobiście nie podoba mi się ten pomysł ... Nic w dotacji nie toMutableList()powinno zwracać nowej instancji listy, jeśli instancja wywołująca metodę jest już listą modyfikowalną.
BrunoJCM
4
to nie jest dobra odpowiedź, a na pewno nie właściwa, nie ma gwarancji, że przyszłe implementacje mogą się zmienić, chyba że wyraźnie udokumentowano, że to wywołanie metody zawsze zwróci nową kopię.
Bhargav,
23

Możesz użyć

Lista -> toList ()

Array -> toArray ()

ArrayList -> toArray ()

MutableList -> toMutableList ()


Przykład:

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

drukuj dziennik:

array: 4
arrayCopy: 4
array after remove: 3
arrayCopy after remove: 4
Rasoul Miri
źródło
14

Mogę wymyślić dwa alternatywne sposoby:

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

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

Aktualizacja: dzięki nowemu silnikowi wnioskowania o typie (opt-in w Kotlin 1.3) możemy pominąć ogólny parametr typu w pierwszym przykładzie i mieć to:

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

FYI. Sposobem na włączenie nowego wnioskowania jest użycie kotlinc -Xnew-inference ./SourceCode.ktwiersza poleceń lub kotlin { experimental { newInference 'enable'}Gradle. Aby uzyskać więcej informacji na temat nowego wnioskowania o typie, obejrzyj ten film: KotlinConf 2018 - Nowe wnioskowanie o typie i powiązane funkcje językowe autorstwa Svetlany Isakovej , zwłaszcza `` wnioskowanie dla budowniczych '' w wieku 30 lat

Jacob Wu
źródło
należy podzielić na 2 odpowiedzi imho, ponieważ uważam, że pierwsza jest poprawna, ale drugiej brakuje trochę urody.
Holger Brandl
@Jacob Wu: Byłem zaskoczony, widząc, że symbol * w drugim rozwiązaniu nie spowodował błędu. Co to robi? Wyszukałem z „mnożeniem jednoargumentowym”, ale nic nie znalazłem.
Lensflare
1
@Lensflare * oznacza zniszczenie tablicy na oddzielne elementy, np. MutableListOf (* [1, 2, 3]) oznacza mutableListOf (1, 2, 3), to jest jak operacja odwrotna do vararg
Jacob Wu
1
@Jacob Wu: Dziękuję. Dzięki Twojej odpowiedzi dowiedziałem się, że operator nazywa się „operatorem rozproszenia”. Widzę, jak to pomaga, łącząc niektóre parametry z tablicą w listę varargs. Ale jakie korzyści daje to w twoim przykładzie? Szybciej czy coś? A może jest to klucz do skopiowania kolekcji?
Lensflare
@Lensflare Myślę, że korzyścią jest tylko składnia - kod jest krótki i nie jest wymagany żaden wyraźny typ ogólny (jak w moim pierwszym przykładzie). Za kulisami uważam, że kod jest kompilowany do operacji tablicowych, więc wydajność powinna być taka sama.
Jacob Wu,
10

Jeśli twoja lista zawiera klasę danych kotlin , możesz to zrobić

selectedSeries = ArrayList(series.map { it.copy() })
Levon Petrosyan
źródło
9

Możesz skorzystać z podanego rozszerzenia, Iterable.toMutableList()które zapewni Ci nową listę. Niestety, jak sugeruje jego podpis i dokumentacja , ma to na celu zapewnienie, że an Iterablejest List(podobnie jak toStringi wiele innych to<type>metod). Nic nie gwarantuje, że będzie to nowa lista. Na przykład dodanie następującego wiersza na początku rozszerzenia: if (this is List) return thisjest uzasadnioną poprawą wydajności (jeśli rzeczywiście poprawia wydajność).

Ponadto ze względu na nazwę otrzymany kod nie jest zbyt jasny.

Wolę dodać własne rozszerzenie, aby mieć pewność wyniku i stworzyć znacznie bardziej przejrzysty kod (tak jak w przypadku tablic ):

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

Zauważ, że addAlljest to najszybszy sposób kopiowania, ponieważ używa natywnego System.arraycopyw implementacji ArrayList.

Uważaj również, że da ci to tylko płytką kopię .

Sir Codesalot
źródło
Podoba mi się to rozwiązanie. Czy nie powinno addAll(this@copyOf), bo thiswewnątrz applybędzie odnosić się do nowo utworzonej pustej listy? Albo to, albo mutableListOf<T>().also { it.addAll(this) }?
Franko Leon Tokalić
5

Sugeruję, aby uzyskać płytką kopię

.map{it}

To zadziała w przypadku wielu typów kolekcji.

Flara obiektywu
źródło
1
Zauważ, że to nie działa dla Maps. Kompiluje się, ale ponieważ itjest a Map.Entry, a kopia jest płytka, masz te same wpisy.
noamtm
1
@noamtm tak, to właśnie mam na myśli w przypadku płytkiej kopii. Ta metoda nigdy nie skopiuje wpisów. Utworzy tylko kopię kolekcji z tymi samymi wpisami. Mapa nie jest tutaj niczym specjalnym.
Lensflare
2
Chodzi mi o to, że chociaż kuszące jest używanie go również na mapach, a kompiluje się i wydaje się działać - tak naprawdę nie działa.
noamtm
4

Podobnie jak w Javie:

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)

Zakładając, że celujesz w JVM (lub Android); Nie jestem pewien, czy działa dla innych celów, ponieważ opiera się na konstruktorach kopiujących ArrayList i HashMap.

noamtm
źródło
2

Chciałbym skorzystać z toCollection()metody rozszerzenie :

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

Spowoduje to utworzenie nowego, MutableLista następnie dodanie każdego elementu oryginału do nowo utworzonej listy.

Wywnioskowanym typem będzie tutaj MutableList<String>. Jeśli nie chcesz ujawniać zmienności tej nowej listy, możesz jawnie zadeklarować typ jako niezmienną listę:

val copy: List<String> = original.toCollection(mutableListOf())
Ben P.
źródło
0

Dla prostych list ma wiele poprawnych rozwiązań powyżej.

Jednak jest to tylko dla płytkich list.

Poniższa funkcja działa dla wszystkich dwuwymiarowych ArrayList. ArrayListjest w praktyce odpowiednikiem MutableList. Co ciekawe, nie działa przy użyciu MutableListtypu jawnego . Jeśli potrzeba więcej wymiarów, konieczne jest wykonanie większej liczby funkcji.

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 dla Matrix liczb całkowitych:

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

to pokazuje 12

Paulo Buchsbaum
źródło
0

Możesz użyć ArrayListkonstruktora:ArrayList(list)

Solomon Ucko
źródło
-1

Wypróbuj poniższy kod do kopiowania listy w Kotlinie

arrayList2.addAll(arrayList1.filterNotNull())
Yyy
źródło