Bagaimana cara membuat salinan duplikat yang tepat dari sebuah array?

100

Bagaimana saya membuat duplikat yang tepat dari sebuah array?

Saya mengalami kesulitan menemukan informasi tentang menduplikasi array di Swift.

Saya mencoba menggunakan .copy()

var originalArray = [1, 2, 3, 4]
var duplicateArray = originalArray.copy()
Patrick
sumber
5
mengapa Anda tidak memberikan nilai secara langsung seperti ini:var duplicateArray = originalArray
Dharmesh Kheni
1
Itu tidak berhasil dalam kasus saya. Itu membuat objek lain yang hanya referensi ke array yang sama dan Anda berakhir dengan 2 variabel yang mereferensikan array yang sama.
pengguna1060500

Jawaban:

176

Array memiliki semantik nilai penuh di Swift, jadi tidak perlu sesuatu yang mewah.

var duplicateArray = originalArray adalah semua yang Anda butuhkan.


Jika konten array Anda adalah tipe referensi, maka ya, ini hanya akan menyalin pointer ke objek Anda. Untuk melakukan salinan mendalam dari konten, Anda akan menggunakan mapdan melakukan salinan dari setiap contoh. Untuk kelas Foundation yang sesuai dengan NSCopyingprotokol, Anda dapat menggunakan copy()metode ini:

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

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

Perhatikan bahwa ada masalah di sini yang semantik nilai Swift bekerja untuk melindungi Anda dari — misalnya, karena NSArraymewakili larik yang tidak dapat diubah, copymetodenya hanya mengembalikan referensi ke dirinya sendiri, sehingga pengujian di atas akan menghasilkan hasil yang tidak terduga.

Nate Cook
sumber
Saya mencoba ini di taman bermain dengan kode sederhana ini var x = [UIView(), UIView(), UIView()] var y = x for i in x { NSLog("%p", i) } println("---") for i in y { NSLog("%p", i) }dan saya mendapatkan output ini: 0x7fa82b0009e0 0x7fa82b012660 0x7fa82b012770 ---0x7fa82b0009e0 0x7fa82b012660 0x7fa82b012770 Sepertinya tidak disalin, tahukah Anda mengapa?
Phil Niedertscheider
@PNGamingPower: x berisi alamat. y berisi salinan dari alamat tersebut. Jika Anda mengubah x [0], y [0] tidak akan berubah. (coba x [0] = x [1], y [0] tidak akan berubah). Jadi, y adalah salinan dalam dari x. Tapi Anda hanya menyalin penunjuknya, bukan yang dituju.
ragnarius
@ragnarius jadi pada dasarnya kita harus mendefinisikan apa arti "copy", baik menyalin pointer atau nilainya. Oleh karena itu, ini adalah solusi untuk menyalin / menduplikasi array pointer, tetapi bagaimana Anda menduplikasi array nilai? Tujuannya akan x[0] == x[1]tetapi x[0] === y[0]harus gagal
Phil Niedertscheider
Ini harus menjadi jawaban yang diterima, karena nilai semantik dari Array membuat "salinan" dari array tidak diperlukan.
Scott Ahten
Ini tidak berhasil untuk saya. Jika saya mengikuti metode itu, saya mendapatkan dua referensi yang akhirnya menunjuk ke array objek yang sama. Jika saya menghapus item dari daftar, itu tercermin di kedua referensi objek karena daftar tidak disalin, melainkan objek hanya direferensikan.
pengguna1060500
28

Nate benar. Jika Anda bekerja dengan array primitif, yang perlu Anda lakukan adalah menetapkan duplikatArray ke originalArray.

Demi kelengkapan, jika Anda mengerjakan objek NSArray, Anda akan melakukan hal berikut untuk melakukan salinan lengkap NSArray:

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

var duplicateArray = NSArray(array:originalArray, copyItems: true)
applejack42.dll
sumber
Ini bagus! Terima kasih!
Patrick
23

Ada pilihan ketiga untuk jawaban Nate:

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

* DIEDIT * pengeditan dimulai di sini

Di atas pada dasarnya sama seperti di bawah ini dan sebenarnya menggunakan operator persamaan di bawah ini akan bekerja lebih baik karena array tidak akan disalin kecuali diubah (ini sesuai desain).

let z = x

Baca lebih lanjut di sini: https://developer.apple.com/swift/blog/?id=10

* DIEDIT * edit berakhir di sini

menambahkan atau menghapus ke array ini tidak akan mempengaruhi array asli. Namun, mengubah setiap properti objek yang dipegang oleh array akan terlihat dalam array asli. Karena objek dalam array bukan salinan (dengan asumsi array menampung objek, bukan angka primitif).

oyalhi
sumber
itu berpengaruh, saya telah mengujinya. ada dua larik, jika Anda mengubah 1, larik kedua akan terpengaruh
Filthy Knight
1
Tidak, tidak, kecuali array memiliki tipe primitif, bukan objek. Maka itu berpengaruh seperti yang dinyatakan dalam jawaban. Kasus uji sederhana:var array1: [String] = ["john", "alan", "kristen"]; print(array1); var array2 = array1.map { $0 }; print(array2); array2[0] = "james"; print(array1); print(array2);
oyalhi
1
Silakan lihat inti yang saya buat untuk contoh yang lebih baik menggunakan kelas khusus: gist.github.com/oyalhi/3b9a415cf20b5b54bb3833817db059ce
oyalhi
Jika kelas Anda mendukung NSCopying, maka duplikat sebuah array:let z = x.map { $0.copy as! ClassX }
John Pang
Jika menggunakan BufferPointers Swift, ini adalah versi yang harus Anda gunakan sebagai salinan langsungnya. Sebelum mengubah nilai dalam array asli atau yang disalin, Swift akan menyalin nilai-nilai asli ke salinan dan kemudian melanjutkan. Jika Anda menggunakan Pointer sebagai gantinya, Swift tidak akan sekarang jika atau ketika perubahan terjadi, sehingga Anda berpotensi mengubah kedua larik.
Justin Ganzer
16

Untuk objek normal yang bisa dilakukan adalah mengimplementasikan protokol yang mendukung penyalinan, dan membuat kelas objek mengimplementasikan protokol ini seperti ini:

protocol Copying {
    init(original: Self)
}

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

Dan kemudian ekstensi Array untuk kloning:

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

dan hanya itu saja, untuk melihat kode dan contoh, periksa intinya

Sohayb Hassoun
sumber
Ini menghemat banyak waktu, Terima kasih.
Abhijit
Untuk subclass, protokol tidak dapat menjamin bahwa persyaratan init diimplementasikan dengan jenis subclass tersebut. Anda mendeklarasikan protokol penyalinan yang mengimplementasikan salinan untuk Anda, tetapi Anda masih mengimplementasikan clone (), itu tidak masuk akal.
Binarian
1
Salinan @iGodric adalah untuk elemen dalam koleksi, dan klon untuk seluruh koleksi, tersedia saat salinan diterapkan untuk elemennya. Masuk akal? Selain itu, kompilator memastikan bahwa subclass mengikuti protokol yang dibutuhkan oleh induknya.
johnbakers
@ Johnbakers Oh ya, sekarang saya melihatnya. Terima kasih atas penjelasannya.
Binarian
Implementasi yang sangat bersih dan menghindari kesibukan yang tidak perlu untuk meneruskan parameter apa pun dalam object'sfungsi init
Sylvan D Ash
0

Jika Anda ingin menyalin item dari array dari beberapa objek kelas. Kemudian Anda dapat mengikuti kode di bawah ini tanpa menggunakan protokol NSCopying tetapi Anda harus memiliki metode init yang harus mengambil semua parameter yang diperlukan untuk objek Anda. Berikut adalah kode untuk contoh untuk diuji di taman bermain.

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
sumber