Wie erstelle ich eine exakte Kopie eines Arrays?

100

Wie würde ich ein Array exakt duplizieren?

Es fällt mir schwer, Informationen zum Duplizieren eines Arrays in Swift zu finden.

Ich habe es versucht .copy()

var originalArray = [1, 2, 3, 4]
var duplicateArray = originalArray.copy()
Patrick
quelle
5
Warum weisen Sie den Wert nicht direkt so zu:var duplicateArray = originalArray
Dharmesh Kheni
1
Das funktioniert in meinem Fall nicht. Dadurch wird ein weiteres Objekt erstellt, das nur auf dasselbe Array verweist, und Sie erhalten zwei Variablen, die auf dasselbe Array verweisen.
user1060500

Antworten:

176

Arrays haben in Swift eine vollständige Semantik, sodass nichts Besonderes erforderlich ist.

var duplicateArray = originalArray ist alles, was du brauchst.


Wenn der Inhalt Ihres Arrays ein Referenztyp ist, werden die Zeiger nur auf Ihre Objekte kopiert. Um eine tiefe Kopie des Inhalts zu erstellen, würden Sie stattdessen verwendenmap eine Kopie jeder Instanz und ausführen. Für Foundation-Klassen, die dem NSCopyingProtokoll entsprechen, können Sie die folgende copy()Methode verwenden:

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

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

Beachten Sie, dass es hier Fallstricke gibt, vor denen die Wertsemantik von Swift Sie schützt. Da NSArraysie beispielsweise ein unveränderliches Array darstellt, gibt ihre copyMethode nur einen Verweis auf sich selbst zurück, sodass der obige Test zu unerwarteten Ergebnissen führen würde.

Nate Cook
quelle
Ich habe dies auf dem Spielplatz mit diesem einfachen Code versucht var x = [UIView(), UIView(), UIView()] var y = x for i in x { NSLog("%p", i) } println("---") for i in y { NSLog("%p", i) }und habe folgende Ausgabe erhalten: 0x7fa82b0009e0 0x7fa82b012660 0x7fa82b012770 ---0x7fa82b0009e0 0x7fa82b012660 0x7fa82b012770 Sieht nicht so aus, als würde es kopiert, wissen Sie warum?
Phil Niedertscheider
@PNGamingPower: x enthält Adressen. y enthält Kopien dieser Adressen. Wenn Sie x [0] ändern, ändert sich y [0] nicht. (versuchen Sie x [0] = x [1], y [0] ändert sich nicht). Somit ist y eine tiefe Kopie von x. Sie haben jedoch nur die Zeiger kopiert, nicht das, worauf sie zeigen.
Ragnarius
@ragnarius, also müssen wir im Grunde definieren, was "Kopieren" bedeutet, entweder den Zeiger oder den Wert kopieren. Daher ist dies die Lösung zum Kopieren / Duplizieren des Array von Zeigern. Wie duplizieren Sie das Array von Werten? Das Ziel wäre x[0] == x[1]aber x[0] === y[0]sollte scheitern
Phil Niedertscheider
Dies sollte die akzeptierte Antwort sein, da die Wertesemantik des Arrays eine "Kopie" des Arrays unnötig macht.
Scott Ahten
Das funktioniert bei mir nicht. Wenn ich dieser Methode folge, erhalte ich zwei Referenzen, die auf dasselbe Array von Objekten verweisen. Wenn ich ein Element aus der Liste entferne, wird es in beiden Objektreferenzen wiedergegeben, da die Liste nicht kopiert wurde, sondern nur auf das Objekt verwiesen wurde.
user1060500
28

Nate ist richtig. Wenn Sie mit primitiven Arrays arbeiten, müssen Sie dem OriginalArray lediglich duplicateArray zuweisen.

Der Vollständigkeit halber würden Sie, wenn Sie ein NSArray-Objekt bearbeiten, die folgenden Schritte ausführen, um eine vollständige Kopie eines NSArray zu erstellen:

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

var duplicateArray = NSArray(array:originalArray, copyItems: true)
applejack42
quelle
Das ist toll! Danke dir!
Patrick
23

Es gibt eine dritte Option für Nates Antwort:

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

* BEARBEITET * Bearbeitung beginnt hier

Oben ist im Wesentlichen dasselbe wie unten, und die Verwendung des unten stehenden Gleichheitsoperators führt zu einer besseren Leistung, da das Array nur kopiert wird, wenn es geändert wird (dies ist beabsichtigt).

let z = x

Lesen Sie hier mehr: https://developer.apple.com/swift/blog/?id=10

* BEARBEITET * Bearbeitung endet hier

Das Hinzufügen oder Entfernen zu diesem Array wirkt sich nicht auf das ursprüngliche Array aus. Das Ändern der Eigenschaften eines Objekts, die das Array enthält, wird jedoch im ursprünglichen Array angezeigt. Da die Objekte im Array keine Kopien sind (vorausgesetzt, das Array enthält Objekte, keine primitiven Zahlen).

oyalhi
quelle
es wirkt, ich habe es getestet. Es gibt zwei Arrays, wenn Sie in 1 ändern, wird das zweite bewirkt
Filthy Knight
1
Nein, es sei denn, das Array enthält primitive Typen anstelle von Objekten. Dann wirkt es sich wie in der Antwort angegeben aus. Ein einfacher Testfall:var array1: [String] = ["john", "alan", "kristen"]; print(array1); var array2 = array1.map { $0 }; print(array2); array2[0] = "james"; print(array1); print(array2);
Oyalhi
1
Bitte sehen Sie sich dieses Gist an, das ich mit einer benutzerdefinierten Klasse erstellt habe: gist.github.com/oyalhi/3b9a415cf20b5b54bb3833817db059ce
oyalhi
Wenn Ihre Klasse unterstützt NSCopying, dann duplizieren Sie ein Array:let z = x.map { $0.copy as! ClassX }
John Pang
Wenn Sie Swifts BufferPointers verwenden, ist dies die Version, die Sie als direkte Kopie verwenden sollten. Bevor Sie einen Wert im Original oder im kopierten Array ändern, kopiert Swift die Werte des Originals in die Kopie und fährt dann fort. Wenn Sie stattdessen Zeiger verwenden, wird Swift dies jetzt nicht tun, wenn oder wenn Änderungen vorgenommen werden. Daher können Sie möglicherweise beide Arrays ändern.
Justin Ganzer
16

Für normale Objekte kann ein Protokoll implementiert werden, das das Kopieren unterstützt, und die Objektklasse implementiert dieses Protokoll wie folgt:

protocol Copying {
    init(original: Self)
}

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

Und dann die Array-Erweiterung zum Klonen:

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

und das ist so ziemlich alles , um Code und ein Beispiel anzusehen, überprüfen Sie dieses Wesentliche

Sohayb Hassoun
quelle
Das hat viel Zeit gespart, danke.
Abhijit
Für Unterklassen kann das Protokoll nicht garantieren, dass die Anforderung init mit dem Typ der Unterklasse implementiert ist. Sie deklarieren ein Kopierprotokoll, das das Kopieren für Sie implementiert, aber Sie implementieren immer noch clone (), was keinen Sinn ergibt.
Binarian
1
@ iGodric copy ist für Elemente in der Sammlung und clone ist für die gesamte Sammlung verfügbar. Dies ist verfügbar, wenn copy für seine Elemente implementiert ist. Sinn ergeben? Außerdem stellt der Compiler sicher, dass die Unterklassen dem Protokoll folgen, das ihre Eltern benötigen.
Johnbakers
@ Johnbakers Oh ja, jetzt sehe ich es. Danke für die Erklärung.
Binarian
Sehr saubere Implementierung und vermeidet die unnötige Hektik der Übergabe von Parametern in der object'sInit-Funktion
Sylvan D Ash
0

Wenn Sie die Elemente eines Arrays eines Klassenobjekts kopieren möchten. Dann können Sie dem folgenden Code folgen, ohne das NSCopying-Protokoll zu verwenden. Sie benötigen jedoch eine Init-Methode, die alle für Ihr Objekt erforderlichen Parameter übernimmt. Hier ist der Code für ein Beispiel zum Testen auf einem Spielplatz.

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
quelle