Come faccio a creare una copia esatta di un array?


100

Come farei un duplicato esatto di un array?

Ho difficoltà a trovare informazioni sulla duplicazione di un array in Swift.

Ho provato a usare .copy()

var originalArray = [1, 2, 3, 4]
var duplicateArray = originalArray.copy()

5
perché non assegni il valore direttamente in questo modo:var duplicateArray = originalArray
Dharmesh Kheni

1
Non funziona nel mio caso. Ciò crea un altro oggetto che è solo un riferimento allo stesso array e si finisce con 2 variabili che fanno riferimento allo stesso array.
user1060500

Risposte:


176

Gli array hanno una semantica a pieno valore in Swift, quindi non c'è bisogno di niente di speciale.

var duplicateArray = originalArray é tutto quello di cui hai bisogno.


Se il contenuto del tuo array è un tipo di riferimento, allora sì, questo copierà solo i puntatori ai tuoi oggetti. Per eseguire una copia completa dei contenuti, dovresti invece utilizzare maped eseguire una copia di ogni istanza. Per le classi Foundation conformi al NSCopyingprotocollo, puoi utilizzare il copy()metodo:

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

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

Nota che qui ci sono insidie ​​da cui la semantica dei valori di Swift sta lavorando per proteggerti: ad esempio, poiché NSArrayrappresenta un array immutabile, il suo copymetodo restituisce solo un riferimento a se stesso, quindi il test sopra produrrebbe risultati inaspettati.


L'ho provato in playground con questo semplice codice var x = [UIView(), UIView(), UIView()] var y = x for i in x { NSLog("%p", i) } println("---") for i in y { NSLog("%p", i) }e ho ottenuto questo output: 0x7fa82b0009e0 0x7fa82b012660 0x7fa82b012770 ---0x7fa82b0009e0 0x7fa82b012660 0x7fa82b012770 Non sembra che venga copiato, sai perché?
Phil Niedertscheider

@PNGamingPower: x contiene indirizzi. y contiene copie di questi indirizzi. Se si modifica x [0], y [0] non cambierà. (prova x [0] = x [1], y [0] non cambierà). Pertanto, y è una copia completa di x, ma hai copiato solo i puntatori, non ciò a cui puntano.
ragnarius

@ragnarius quindi in pratica dobbiamo definire cosa significa "copia", copiando il puntatore o il valore. Quindi questa è la soluzione per copiare / duplicare l'array di puntatori, ma come duplicare l'array di valori? L'obiettivo sarebbe x[0] == x[1]ma x[0] === y[0]dovrebbe fallire
Phil Niedertscheider

Questa dovrebbe essere la risposta accettata, poiché la semantica del valore di Array rende non necessaria una "copia" dell'array.
Scott Ahten

Questo non funziona per me. Se seguo questo metodo, ottengo due riferimenti che finiscono per indicare lo stesso array di oggetti. Se rimuovo un elemento dall'elenco, viene riflesso in entrambi i riferimenti all'oggetto poiché l'elenco non è stato copiato, ma piuttosto l'oggetto è stato semplicemente referenziato.
user1060500

28

Nate ha ragione. Se stai lavorando con array primitivi, tutto ciò che devi fare è assegnare duplicateArray a originalArray.

Per ragioni di completezza, se stavi lavorando su un oggetto NSArray, dovresti fare quanto segue per fare una copia completa di un NSArray:

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

var duplicateArray = NSArray(array:originalArray, copyItems: true)

È fantastico! Grazie!
Patrick

23

C'è una terza opzione per la risposta di Nate:

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

* MODIFICATO * modifica inizia qui

Sopra è essenzialmente lo stesso come sotto e in realtà l'utilizzo dell'operatore di uguaglianza di seguito funzionerà meglio poiché l'array non verrà copiato a meno che non venga modificato (questo è di progettazione).

let z = x

Maggiori informazioni qui: https://developer.apple.com/swift/blog/?id=10

* MODIFICATO * modifica termina qui

l'aggiunta o la rimozione a questo array non influirà sull'array originale. Tuttavia, la modifica di qualsiasi proprietà degli oggetti contenuta nell'array verrebbe visualizzata nell'array originale. Perché gli oggetti nell'array non sono copie (supponendo che l'array contenga oggetti, non numeri primitivi).


ha effetto, l'ho testato. ci sono due array, se si cambia in 1, viene effettuato il secondo
Filthy Knight

1
No, a meno che l'array non contenga tipi primitivi invece di oggetti. Quindi influisce come indicato nella risposta. Un semplice test case:var array1: [String] = ["john", "alan", "kristen"]; print(array1); var array2 = array1.map { $0 }; print(array2); array2[0] = "james"; print(array1); print(array2);
oyalhi

1
Si prega di vedere questa sintesi che ho creato per un esempio migliore utilizzando una classe personalizzata: gist.github.com/oyalhi/3b9a415cf20b5b54bb3833817db059ce
oyalhi

Se la tua classe supporta NSCopying, duplica un array:let z = x.map { $0.copy as! ClassX }
John Pang

Se usi BufferPointer di Swift, questa è la versione che dovresti usare come copia diretta. Prima di modificare un valore nell'originale o nell'array copiato, Swift copia i valori dell'originale sulla copia e poi continua. Se invece utilizzi Puntatori, Swift non lo farà ora se o quando vengono apportate modifiche, quindi potresti potenzialmente finire per cambiare entrambi gli array.
Justin Ganzer

16

Per oggetti normali ciò che si può fare è implementare un protocollo che supporti la copia e fare in modo che la classe dell'oggetto implementi questo protocollo in questo modo:

protocol Copying {
    init(original: Self)
}

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

E poi l'estensione Array per la clonazione:

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

e questo è più o meno tutto, per visualizzare il codice e un esempio, controllare questa sintesi


Questo ha risparmiato molto tempo, grazie.
Abhijit

Per le sottoclassi il protocollo non può garantire che il requisito init sia implementato con il tipo della sottoclasse. Stai dichiarando un protocollo di copia che implementa la copia per te, ma stai ancora implementando clone (), che non ha senso.
Binarian

1
La copia @iGodric è per gli elementi nella raccolta e il clone è per l'intera raccolta, disponibile quando la copia è implementata per i suoi elementi. Ha senso? Inoltre, il compilatore garantisce che le sottoclassi seguano il protocollo richiesto dal loro genitore.
johnbakers

@johnbakers Oh sì, ora lo vedo. Grazie per la spiegazione.
Binarian

Implementazione molto pulita ed evita il trambusto inutile di passare qualsiasi parametro nella object'sfunzione init
Sylvan D Ash

0

Se vuoi copiare gli elementi di un array di qualche oggetto di classe. Quindi puoi seguire il codice seguente senza utilizzare il protocollo NSCopying ma devi disporre di un metodo init che dovrebbe accettare tutti i parametri richiesti per il tuo oggetto. Ecco il codice per un esempio da testare su 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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.