web-dev-qa-db-ja.com

swiftの配列を比較します

Swiftが配列を比較する方法を理解しようとしています。

var myArray1 : [String] = ["1","2","3","4","5"]
var myArray2 : [String] = ["1","2","3","4","5"]

// 1) Comparing 2 simple arrays

if(myArray1 == myArray2) {
    println("Equality")
} else {
    println("Equality no")
}
// -> prints equality -> thanks god

// 2) comparing to a "copy" of an array

// Swift copies arrays when passed as parameters (as per doc)
func arrayTest(anArray: [String]) -> Bool {
    return anArray == myArray1
}

println("Array test 1 is \(arrayTest(myArray1))")
println("Array test 2 is \(arrayTest(myArray2))")
// equality works for both

myArray2.append("test")
println("Array test 2 is \(arrayTest(myArray2))")
// false (obviously)

myArray2.removeAtIndex(5)
println("Array test 2 is \(arrayTest(myArray2))")
// true

Appleは、アレイのコピーに関して、舞台裏で最適化が行われていると言います。時々-常にではない-構造が実際にコピーされるかどうかのように見えます。

とはいえ、

1)==すべての配列を反復処理して、要素ベースの比較を実行しますか? (そのように見えます)->それでは、非常に大きなアレイのパフォーマンス/メモリ使用量はどうですか?

2)すべての要素が等しい場合、==は常にtrueを返すと確信していますか? Java文字列で==の悪い思い出がある

3)myArray1とmyArray2が技術的に同じ「メモリ位置」/ポインタ/などを使用しているかどうかを確認する方法はありますか?最適化の仕組みと潜在的な警告を理解した後です。

ありがとう。

57
vivien.destpern

==について少し緊張するのは当然です。

struct NeverEqual: Equatable { }
func ==(lhs: NeverEqual, rhs: NeverEqual)->Bool { return false }
let x = [NeverEqual()]
var y = x
x == y  // this returns true

[NeverEqual()] == [NeverEqual()] // false
x == [NeverEqual()] // false

let z = [NeverEqual()]
x == z // false

x == y // true

y[0] = NeverEqual()
x == y // now false

どうして? Swift配列はEquatableに準拠していませんが、標準ライブラリで次のように定義されている==演算子があります。

func ==<T : Equatable>(lhs: [T], rhs: [T]) -> Bool

この演算子は、lhsおよびrhsの要素をループ処理し、各位置の値を比較します。 notビット単位の比較を行います–要素の各ペアで==演算子を呼び出します。つまり、要素にカスタム==を記述すると、その要素が呼び出されます。

ただし、最適化が含まれています。2つの配列の基礎となるバッファーが同じである場合、気にせず、単にtrueを返します(これらは同じ要素を含んでいます。もちろん同じです!)。

この問題は、完全にNeverEqual等価演算子の問題です。平等は推移的、対称的、再帰的である必要があり、これは再帰的ではありません(x == xはfalseです)。しかし、それでもまだ気づかないことがあります。

Swift配列はコピーオンライトです。したがって、var x = yを書き込むとき、実際に配列のコピーを作成するのではなく、xのストレージバッファーポインターをyに向けるだけです。 xまたはyが後で変更された場合にのみ、バッファのコピーが作成されるため、変更されていない変数は影響を受けません。これは、配列が値型のように振る舞いながら、パフォーマンスを維持するために重要です。

Swiftの初期バージョンでは、実際に配列で===を呼び出すことができました(初期バージョンでも、xを変更した場合、yも変更される場合、変更の動作は少し異なりました。 letで宣言されていたので、人々を驚かせたので変更しました)。

この(トリックの突っ込みと突き出しの調査を除いて依存しない非常に実装依存の)配列で===の古い動作をちょっと再現できます:

let a = [1,2,3]
var b = a

a.withUnsafeBufferPointer { outer in 
    b.withUnsafeBufferPointer { inner in 
        println(inner.baseAddress == outer.baseAddress) 
    } 
}
69

Swiftの==はJavaのequals()と同じで、値を比較します。

Swiftの===はJavaの==と同じで、参照を比較します。

Swiftでは、配列コンテンツの値を次のように簡単に比較できます。

["1", "2"] == ["1", "2"]

ただし、参照を比較する場合、これは機能しません。

var myArray1 = [NSString(string: "1")]
var myArray2 = [NSString(string: "1")]

myArray1[0] === myArray2[0] // false
myArray1[0] == myArray2[0] // true

答えは:

  1. パフォーマンスは、値(参照ではなく)の比較を行うのに最適だと思います
  2. はい、値を比較する場合
  3. Swift配列は値型であり、参照型ではありません。したがって、メモリの場所は、それ自体と比較した場合(または安全でないポインタを使用した場合)にのみ同じです
18
Kirsteins

どのように比較したいかによります。例:["1", "2"] == ["1", "2"] // true but ["1", "2"] == ["2", "1"] // false

その2番目のケースもtrueである必要があり、繰り返し値を無視しても大丈夫な場合は、以下を実行できます。Set(["1", "2"]) == Set(["2", "1"]) // true(Swift 2にNSSetを使用)

6
demosten

カスタムオブジェクトの配列を比較するには、 elementsEqual を使用できます。

class Person {

    let ID: Int!
    let name: String!

    init(ID: Int, name: String) {

        self.ID = ID
        self.name = name
    }
}

let oldFolks = [Person(ID: 1, name: "Ann"), Person(ID: 2, name: "Tony")]
let newFolks = [Person(ID: 2, name: "Tony"), Person(ID: 4, name: "Alice")]

if oldFolks.elementsEqual(newFolks, by: { $0.ID == $1.ID }) {

    print("Same people in same order")

} else {

    print("Nope")
}
5
tier777

配列はSwift 4.1のEquatableに準拠しており、前の回答で述べた警告を無効にします。これはXcode 9.3で利用可能です。

https://Swift.org/blog/conditional-conformance/

しかし、==を実装したからといって、ArrayOptionalEquatableに準拠しているという意味ではありませんでした。これらの型は非等価型を格納できるため、赤字可能な型を格納する場合にのみ赤字可能であることを表現できる必要がありました。

つまり、これらの==演算子には大きな制限があり、2レベルの深さでは使用できませんでした。

条件付き適合により、これを修正できます。これらの型が等しい場合は、既に定義されている==演算子を使用して、これらの型がEquatableに準拠することを記述できます。

4
Greg McCoy

array of custom objectsがある場合、少なくともSwift 4.1では、同等性テストに注意する必要があります。
カスタムオブジェクトがnotNSObjectのサブクラスである場合、比較ではstatic func == (lhs: Nsobject, rhs: Nsobject) -> Boolを使用します。これは定義する必要があります。
isNSObjectのサブクラスの場合、func isEqual(_ object: Any?) -> Boolを使用しますが、これはオーバーライドする必要があります。

次のコードを確認し、すべてのreturnステートメントにブレークポイントを設定してください。

class Object: Equatable {

    static func == (lhs: Object, rhs: Object) -> Bool {
        return true
    }
}

次のクラスは、EquatableからNSObjectを継承します

class Nsobject: NSObject {

    static func == (lhs: Nsobject, rhs: Nsobject) -> Bool {
        return true
    }


    override func isEqual(_ object: Any?) -> Bool {
        return true
    }

}  

以下と比較できます。

let nsObject1 = Nsobject()
let nsObject2 = Nsobject()
let nsObjectArray1 = [nsObject1]
let nsObjectArray2 = [nsObject2]
let _ = nsObjectArray1 == nsObjectArray2

let object1 = Object()
let object2 = Object()
let objectArray1 = [object1]
let objectArray2 = [object2]
let _ = objectArray1 == objectArray2
1