Como faço uma cópia duplicada exata de um array?

100

Como eu faria uma duplicata exata de um array?

Estou tendo dificuldade em encontrar informações sobre a duplicação de uma matriz em Swift.

Tentei usar .copy()

var originalArray = [1, 2, 3, 4]
var duplicateArray = originalArray.copy()
Patrick
fonte
5
por que você não atribui valor diretamente assim:var duplicateArray = originalArray
Dharmesh Kheni
1
Isso não funciona no meu caso. Isso cria outro objeto que é apenas uma referência ao mesmo array e você acaba com 2 variáveis ​​referenciando o mesmo array.
user1060500

Respostas:

176

Os arrays têm semântica de valor total em Swift, portanto, não há necessidade de nada sofisticado.

var duplicateArray = originalArray é tudo o que você precisa.


Se o conteúdo do seu array for um tipo de referência, então sim, isso apenas copiará os ponteiros para seus objetos. Para realizar uma cópia profunda do conteúdo, você deve usar mape realizar uma cópia de cada instância. Para classes Foundation que estão em conformidade com o NSCopyingprotocolo, você pode usar o copy()método:

let x = [NSMutableArray(), NSMutableArray(), NSMutableArray()]
let y = x
let z = x.map { $0.copy() }

x[0] === y[0]   // true
x[0] === z[0]   // false

Observe que há armadilhas aqui contra as quais a semântica de valor do Swift está trabalhando para protegê-lo - por exemplo, como NSArrayrepresenta uma matriz imutável, seu copymétodo apenas retorna uma referência para si mesmo, portanto, o teste acima produziria resultados inesperados.

Nate Cook
fonte
Tentei fazer isso no playground com um código simples var x = [UIView(), UIView(), UIView()] var y = x for i in x { NSLog("%p", i) } println("---") for i in y { NSLog("%p", i) }e obtive esta saída: 0x7fa82b0009e0 0x7fa82b012660 0x7fa82b012770 ---0x7fa82b0009e0 0x7fa82b012660 0x7fa82b012770 Não parece que está sendo copiado, sabe por quê?
Phil Niedertscheider
@PNGamingPower: x contém endereços. y contém cópias desses endereços. Se você alterar x [0], y [0] não mudará. (tente x [0] = x [1], y [0] não mudará). Portanto, y é uma cópia profunda de x. Mas você apenas copiou os ponteiros, não para onde eles estão apontando.
ragnarius
@ragnarius então basicamente temos que definir o que "copiar" significa, copiando o ponteiro ou o valor. Portanto, esta é a solução para copiar / duplicar a matriz de ponteiros, mas como você duplica a matriz de valores? O objetivo seria, x[0] == x[1]mas x[0] === y[0]deve falhar
Phil Niedertscheider
Essa deve ser a resposta aceita, já que a semântica de valor do Array torna desnecessária uma "cópia" do array.
Scott Ahten
Isso não funciona para mim. Se eu seguir esse método, obtenho duas referências que acabam apontando para a mesma matriz de objetos. Se eu remover um item da lista, ele será refletido em ambas as referências de objeto, uma vez que a lista não foi copiada, mas sim o objeto apenas referenciado.
user1060500
28

Nate está correto. Se estiver trabalhando com matrizes primitivas, tudo o que você precisa fazer é atribuir duplicateArray ao originalArray.

Para fins de integridade, se você estivesse trabalhando em um objeto NSArray, faria o seguinte para fazer uma cópia completa de um NSArray:

var originalArray = [1, 2, 3, 4] as NSArray

var duplicateArray = NSArray(array:originalArray, copyItems: true)
applejack42
fonte
Isso é ótimo! Obrigado!
Patrick
23

Há uma terceira opção para a resposta de Nate:

let z = x.map { $0 }  // different array with same objects

* EDITADO * a edição começa aqui

Acima é essencialmente o mesmo que abaixo e, na verdade, o uso do operador de igualdade abaixo terá um desempenho melhor, pois a matriz não será copiada a menos que seja alterada (isso é por design).

let z = x

Leia mais aqui: https://developer.apple.com/swift/blog/?id=10

* EDITADO * a edição termina aqui

adicionar ou remover a esta matriz não afetará a matriz original. No entanto, a alteração de qualquer uma das propriedades dos objetos que a matriz contém seria vista na matriz original. Porque os objetos na matriz não são cópias (assumindo que a matriz contém objetos, não números primitivos).

oyalhi
fonte
faz efeito, eu testei. existem duas matrizes, se u mudar em 1, a segunda é efetuada
Filthy Knight
1
Não, a menos que array contenha tipos primitivos em vez de objetos. Então, isso afeta conforme declarado na resposta. Um caso de teste simples:var array1: [String] = ["john", "alan", "kristen"]; print(array1); var array2 = array1.map { $0 }; print(array2); array2[0] = "james"; print(array1); print(array2);
oyalhi
1
Por favor, veja esta essência que criei para um melhor exemplo usando uma classe personalizada: gist.github.com/oyalhi/3b9a415cf20b5b54bb3833817db059ce
oyalhi
Se sua classe apoiar NSCopying, duplique uma matriz:let z = x.map { $0.copy as! ClassX }
John Pang
Se estiver usando BufferPointers do Swift, esta é a versão que você deve usar como cópia direta. Antes de alterar um valor no array original ou copiado, o Swift irá copiar os valores do original para a cópia e então continuar. Se você usar ponteiros em vez disso, o Swift não usará agora se ou quando as alterações ocorrerem, portanto, você pode acabar alterando ambos os arrays.
Justin Ganzer
16

Para objetos normais, o que pode ser feito é implementar um protocolo que suporte cópia, e fazer com que a classe de objeto implemente este protocolo assim:

protocol Copying {
    init(original: Self)
}

extension Copying {
    func copy() -> Self {
        return Self.init(original: self)
    }
}

E então a extensão Array para clonagem:

extension Array where Element: Copying {
    func clone() -> Array {
        var copiedArray = Array<Element>()
        for element in self {
            copiedArray.append(element.copy())
        }
        return copiedArray
    }
}

e isso é basicamente isso, para ver o código e uma amostra, verifique esta essência

Sohayb Hassoun
fonte
Isso economizou muito tempo, obrigado.
Abhijit
Para subclasses, o protocolo não pode garantir que o requisito init seja implementado com o tipo da subclasse. Você está declarando um protocolo de cópia que implementa copy para você, mas ainda está implementando clone (), isso não faz sentido.
Binarian
1
@iGodric copy é para elementos da coleção e clone é para toda a coleção, disponível quando a cópia é implementada para seus elementos. Faz sentido? Além disso, o compilador garante que as subclasses sigam o protocolo que seu pai requer.
johnbakers
@johnbakers Oh sim, agora eu vejo. Obrigado pela explicação.
Binarian
Implementação muito limpa e evita a confusão desnecessária de passar quaisquer parâmetros na object'sfunção init
Sylvan D Ash
0

Se você deseja copiar os itens de um array de algum objeto de classe. Então você pode seguir o código abaixo sem usar o protocolo NSCopying, mas você precisa ter um método init que deve levar todos os parâmetros que são necessários para o seu objeto. Aqui está o código de um exemplo para testar no playground.

class ABC {
    
    var a = 0
    func myCopy() -> ABC {
        
        return ABC(value: self.a)
    }
    
    init(value: Int) {
        
        self.a = value
    }
}

var arrayA: [ABC] = [ABC(value: 1)]
var arrayB: [ABC] = arrayA.map { $0.myCopy() }

arrayB.first?.a = 2
print(arrayA.first?.a)//Prints 1
print(arrayB.first?.a)//Prints 2
Noman Haroon
fonte