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が技術的に同じ「メモリ位置」/ポインタ/などを使用しているかどうかを確認する方法はありますか?最適化の仕組みと潜在的な警告を理解した後です。
ありがとう。
==
について少し緊張するのは当然です。
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)
}
}
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"] == ["1", "2"] // true
but ["1", "2"] == ["2", "1"] // false
その2番目のケースもtrueである必要があり、繰り返し値を無視しても大丈夫な場合は、以下を実行できます。Set(["1", "2"]) == Set(["2", "1"]) // true
(Swift 2にNSSetを使用)
カスタムオブジェクトの配列を比較するには、 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")
}
配列はSwift 4.1のEquatable
に準拠しており、前の回答で述べた警告を無効にします。これはXcode 9.3で利用可能です。
https://Swift.org/blog/conditional-conformance/
しかし、
==
を実装したからといって、Array
やOptional
がEquatable
に準拠しているという意味ではありませんでした。これらの型は非等価型を格納できるため、赤字可能な型を格納する場合にのみ赤字可能であることを表現できる必要がありました。つまり、これらの
==
演算子には大きな制限があり、2レベルの深さでは使用できませんでした。条件付き適合により、これを修正できます。これらの型が等しい場合は、既に定義されている
==
演算子を使用して、これらの型がEquatable
に準拠することを記述できます。
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