配列の正確な複製コピーを作成するにはどうすればよいですか?


100

配列を正確に複製するにはどうすればよいですか?

Swiftでのアレイの複製に関する情報を見つけるのに苦労しています。

使ってみた .copy()

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

5
なぜこのように直接値を割り当てないのですか?var duplicateArray = originalArray
Dharmesh Kheni

1
それは私の場合はうまくいきません。これにより、同じ配列への参照である別のオブジェクトが作成され、同じ配列を参照する2つの変数が作成されます。
user1060500

回答:


176

配列はSwiftで完全な値のセマンティクスを持っているので、特別なことをする必要はありません。

var duplicateArray = originalArray は、あなたが必要とすることすべてです。


配列の内容が参照型である場合、はい、これはオブジェクトへのポインターのみをコピーします。コンテンツのディープコピーを実行するには、代わりmapに各インスタンスのコピーを使用して実行します。NSCopyingプロトコルに準拠するFoundationクラスの場合、copy()メソッドを使用できます。

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

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

Swiftの値のセマンティクスがユーザーを保護するために機能している落とし穴があることに注意してください。たとえば、NSArray不変の配列を表すため、そのcopyメソッドはそれ自体への参照を返すだけなので、上記のテストでは予期しない結果が生じます。


私はこの簡単なコードvar x = [UIView(), UIView(), UIView()] var y = x for i in x { NSLog("%p", i) } println("---") for i in y { NSLog("%p", i) }を使って遊び場でこれを試し、この出力を得ました:0x7fa82b0009e0 0x7fa82b012660 0x7fa82b012770 ---0x7fa82b0009e0 0x7fa82b012660 0x7fa82b012770 コピーされているように見えません、なぜですか?
Phil Niedertscheider 2015

@PNGamingPower:xにはアドレスが含まれます。yにはそれらのアドレスのコピーが含まれます。x [0]を変更しても、y [0]は変更されません。(x [0] = x [1]を試してください。y[0]は変更されません)。したがって、yはxのディープコピーですが、ポインターをコピーしただけで、ポインターが指しているものはコピーしていません。
ragnarius

@ragnariusなので、基本的には「コピー」の意味を定義する必要があります。つまり、ポインターまたは値をコピーします。したがって、これはポインタの配列をコピー/複製するための解決策ですが、値の配列をどのように複製しますか?目標は、x[0] == x[1]しかしx[0] === y[0]失敗するはずです
Phil Niedertscheider

Arrayの値のセマンティクスによって配列の「コピー」が不要になるため、これは受け入れられる答えになるはずです。
スコットアーテン

これは私にはうまくいきません。その方法に従うと、同じオブジェクト配列を指す2つの参照を取得します。リストからアイテムを削除すると、リストはコピーされず、オブジェクトが参照されただけなので、両方のオブジェクト参照に反映されます。
user1060500

28

ネイトは正しいです。プリミティブ配列を使用している場合は、元の配列にduplicateArrayを割り当てるだけです。

完全を期すために、NSArrayオブジェクトを使用している場合は、次のようにしてNSArrayの完全なコピーを作成します。

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

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

これは素晴らしい!ありがとうございました!
Patrick

23

ネイトの答えには3つ目の選択肢があります:

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

* EDITED *編集はここから始まります

上記は基本的に以下と同じですが、配列が変更されない限りこれはコピーされないため(これは仕様による)、実際には以下の等価演算子を使用するとパフォーマンスが向上します。

let z = x

詳細はこちら:https : //developer.apple.com/swift/blog/?id=10

*編集済み*編集はここで終了します

この配列に追加または削除しても、元の配列には影響しません。ただし、配列が保持するオブジェクトのプロパティを変更すると、元の配列に表示されます。配列内のオブジェクトはコピーではないため(配列がプリミティブ番号ではなくオブジェクトを保持していると仮定)


効果があります。テストしました。2つの配列があり、uが1に変化した場合、2番目の配列が影響を受けます
汚い騎士

1
いいえ、配列がオブジェクトではなくプリミティブ型を保持していない限り、そうではありません。次に、回答に記載されているように影響します。簡単なテストケース:var array1: [String] = ["john", "alan", "kristen"]; print(array1); var array2 = array1.map { $0 }; print(array2); array2[0] = "james"; print(array1); print(array2);
oyalhi 2017

1
カスタムクラスを使用してより良い例を作成するには、この要旨を参照してください:gist.github.com/oyalhi/3b9a415cf20b5b54bb3833817db059ce
oyalhi

あなたのクラスのサポート場合NSCopying:、その配列複製let z = x.map { $0.copy as! ClassX }
ジョン・パン

SwiftのBufferPointersを使用している場合、これは直接コピーとして使用するバージョンです。元の配列またはコピーされた配列の値を変更する前に、Swiftは元の値をコピーにコピーしてから続行します。代わりにポインターを使用する場合、Swiftは変更が行われるかどうか、または変更が行われるときに行わないため、両方の配列が変更される可能性があります。
Justin Ganzer

16

通常のオブジェクトの場合、実行できることは、コピーをサポートするプロトコルを実装し、オブジェクトクラスにこのプロトコルを次のように実装させることです。

protocol Copying {
    init(original: Self)
}

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

そして、クローン用のArray拡張機能:

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

コードとサンプルを表示するには、この要点を確認してください


これは多くの時間を節約しました、ありがとう。
Abhijit 2016年

サブクラスの場合、プロトコルは要件initがサブクラスのタイプで実装されることを保証できません。コピーを実装するコピープロトコルを宣言していますが、意味をなさないclone()を実装しています。
Binarian、2016年

1
@iGodricコピーはコレクション内の要素用で、クローンはコレクション全体用です。コピーがその要素に実装されている場合に使用できます。理にかなっていますか?また、コンパイラーは、サブクラスが親が必要とするプロトコルに従うことを保証します。
johnbakers 2017年

@johnbakersああそう、今私はそれを見る。説明ありがとうございます。
Binarian 2017年

非常にクリーンな実装で、object'sinit関数のパラメーターを渡す不必要な煩わしさを回避
Sylvan D Ash

0

クラスオブジェクトの配列のアイテムをコピーしたい場合。次に、NSCopyingプロトコルを使用せずに以下のコードを実行できますが、オブジェクトに必要なすべてのパラメーターを取得するinitメソッドが必要です。遊び場でテストする例のコードは次のとおりです。

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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.