Como clonar ou copiar uma lista no kotlin

103

Como copiar lista em Kotlin?

estou a usar

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

Existe uma maneira mais fácil?

Audi
fonte
1
Acho que sua solução já é a maneira mais fácil, caso você não precise de clonagem profunda.
Serdar Samancıoğlu

Respostas:

146

Isso funciona bem.

val selectedSeries = series.toMutableList()
Audi
fonte
6
val selectedSeries = series.toList()também funciona porque chama toMutableList()em sua implementação.
Flávio Faria
4
@ FlávioFaria acabou de testar com ===e tenho que dizer toList()que não copia a coleção, mas toMutableList()sim
Peppermint Paddy
3
@PeppermintPaddy Ele faz cópia, exceto no caso de listas vazias. Se a fonte estiver vazia, Iterable.toList()retorna emptyList(), que sempre retorna o mesmo objeto (imutável). Portanto, se você testar com, emptyList()obterá o mesmo objeto de volta.
Laurence Gonsalves
52
Eu pessoalmente não gosto dessa ideia ... Nada na concessão de documentos que toMutableList()deva retornar uma nova instância de uma lista se a instância que chama o método já é uma lista mutável.
BrunoJCM
4
esta não é uma boa resposta, e definitivamente não é a certa, não há garantia de que futuras implementações possam mudar, a menos que seja especificamente documentado que esta chamada de método sempre retornará uma nova cópia.
Bhargav
23

Você pode usar

Listar -> toList ()

Array -> toArray ()

ArrayList -> toArray ()

MutableList -> toMutableList ()


Exemplo:

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

imprimir log:

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

Posso propor duas maneiras alternativas:

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

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

Atualização: com o novo mecanismo de inferência de tipo (opcional no Kotlin 1.3), podemos omitir o parâmetro de tipo genérico no primeiro exemplo e ter este:

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

Para sua informação. A maneira de ativar a nova Inferência é kotlinc -Xnew-inference ./SourceCode.ktpor linha de comando ou kotlin { experimental { newInference 'enable'}para Gradle. Para obter mais informações sobre a nova inferência de tipo, confira este vídeo: KotlinConf 2018 - Nova inferência de tipo e recursos de linguagem relacionados por Svetlana Isakova , especialmente 'inferência para construtores' em 30 '

Jacob Wu
fonte
deve ser dividido em 2 respostas imho, pois acho que a primeira está correta, mas a última carece de alguma beleza.
Holger Brandl
@Jacob Wu: Fiquei surpreso ao ver que o símbolo * na segunda solução não produziu um erro. O que isso faz? Fiz uma pesquisa com "multiplicação unária" mas não encontrei nada.
Lensflare
1
@Lensflare * significa destruir uma matriz em itens separados, por exemplo, mutableListOf (* [1, 2, 3]) significa mutableListOf (1, 2, 3), é como a operação oposta a vararg
Jacob Wu
1
@Jacob Wu: Obrigado. Com sua resposta, descobri que a operadora se chama "operadora spread". Eu vejo como isso ajuda combinando alguns parâmetros com uma matriz em uma lista de varargs. Mas que benefício isso tem em seu exemplo? É mais rápido ou algo assim? Ou é a chave para garantir que a coleção seja copiada?
Lensflare
@Lensflare Acho que o benefício é apenas a sintaxe - o código é curto e nenhum tipo genérico explícito é necessário (como no meu primeiro exemplo). Nos bastidores, acredito que o código é compilado para operações de array, portanto, o desempenho deve ser o mesmo.
Jacob Wu
10

Se sua lista contém classes de dados kotlin , você pode fazer isso

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

Você pode usar a extensão fornecida, Iterable.toMutableList()que fornecerá uma nova lista. Infelizmente, como sua assinatura e documentação sugerem, ele serve para garantir que um Iterableé um List(assim como toStringe muitos outros to<type>métodos). Nada garante que será uma lista nova . Por exemplo, adicionar a seguinte linha no início da extensão: if (this is List) return thisé uma melhoria de desempenho legítima (se de fato melhora o desempenho).

Além disso, por causa de seu nome, o código resultante não é muito claro.

Prefiro adicionar minha própria extensão para ter certeza do resultado e criar um código muito mais claro (assim como temos para matrizes ):

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

Observe que addAllé a maneira mais rápida de copiar porque usa o nativo System.arraycopyna implementação de ArrayList.

Além disso, tome cuidado, pois isso só lhe dará uma cópia superficial .

Sir Codesalot
fonte
Eu gosto dessa solução. Não deveria ser addAll(this@copyOf), porque thisdentro applyvai se referir à lista vazia recém-criada? Isso ou mutableListOf<T>().also { it.addAll(this) }?
Franko Leon Tokalić
5

Para uma cópia superficial, sugiro

.map{it}

Isso funcionará para muitos tipos de coleção.

Reflexo de lente
fonte
1
Observe que não funciona para Maps. Ele compila, mas como ité um Map.Entrye a cópia é superficial, você tem as mesmas entradas.
noamtm
1
@noamtm sim, é isso que quero dizer com cópia superficial. Este método nunca copiará as entradas. Só fará uma cópia da coleção com as mesmas entradas. O mapa não é nada especial aqui.
Lensflare de
2
Meu ponto é que, embora seja tentador usá-lo em mapas também, e ele compila e parece funcionar - ele realmente não funciona.
noamtm de
4

Assim como em 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)

Supondo que você esteja visando o JVM (ou Android); Não tenho certeza se funciona para outros destinos, pois se baseia nos construtores de cópia de ArrayList e HashMap.

noamtm
fonte
2

Eu usaria o toCollection()método de extensão :

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

Isso criará um novo MutableListe, em seguida, adicionará cada elemento do original à lista recém-criada.

O tipo inferido aqui será MutableList<String>. Se você não quiser expor a mutabilidade dessa nova lista, pode declarar o tipo explicitamente como uma lista imutável:

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

Para listas simples, há muitas soluções corretas acima.

No entanto, é apenas para listas rasas.

A função abaixo funciona para qualquer bidimensional ArrayList. ArrayListé, na prática, equivalente a MutableList. Curiosamente, não funciona ao usar um MutableListtipo explícito . Se alguém precisa de mais dimensões, é necessário fazer mais funções.

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
}

Demonstração para matriz inteira:

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

isto mostra 12

Paulo Buchsbaum
fonte
0

Você pode usar o ArrayListconstrutor:ArrayList(list)

Solomon Ucko
fonte
-1

Experimente o código abaixo para copiar a lista em Kotlin

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