2014-12-19 119 views
38

試圖理解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 

蘋果公司表示,在陣列副本的背景下有優化。看起來有時 - 並非總是 - 結構實際上是被複制或不是。

這就是說,

1)是==遍歷所有陣列來執行基於元素的比較? (看起來像) - >那麼如何在非常大的數組上使用性能/內存?

2)如果所有元素都相等,我們肯定==會永遠返回true嗎?我在Java字符串上有==的錯誤回憶

3)有沒有辦法檢查myArray1和myArray2是否在技術上使用相同的「內存位置」/指針/等?我瞭解了優化如何工作和潛在的警告。

謝謝。

+0

直接指針比較是'===' – Anorak

+0

不起作用。 ===表示 - > [String]不符合AnyObject –

+0

@Anorak'==='僅用於類,'Array'是一個結構。 – Kirsteins

回答

58

你說得對稍微緊張==

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 

爲什麼?迅速陣列不符合Equatable,但它們確實具有==操作者,在標準庫中定義爲:

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

這個操作符遍歷在lhsrhs的元素,在每個位置進行比較的值。它確實是而不是做了按位比較 - 它在每對元素上調用==運算符。這意味着如果您爲自己的元素編寫自定義==,它會被調用。但它包含一個優化 - 如果兩個數組的基礎緩衝區是相同的,它並不打擾,它只是返回true(它們包含相同的元素,當然它們是相等的!)。

這個問題完全是NeverEqual等號運算符的錯。平等應該是傳遞性的,對稱的和自反性的,而且這不是自反的(x == x是錯誤的)。但它可能仍然不明白。

斯威夫特陣列寫入時複製 - 所以,當你寫var x = y它實際上並沒有做數組的副本,它只是S點x「在y S吸緩衝區指針」。只有當xy稍後發生變化時,纔會複製緩衝區,以便未改變的變量不受影響。這對於數組的行爲類似於值類型但仍具有高性能至關重要。

在斯威夫特的早期版本中,你實際上可以呼籲陣列===(也是在早期版本中,變異的行爲是有點不同的,如果你突變xy也會改變,即使它已被宣佈與let - 這把人們嚇壞了,所以他們改變了)。

可以還挺再現===與此陣舊的行爲(非常依賴於實現不應依賴上除了戳,並督促調查)絕招:

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

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

還有更多的東西比滿足眼睛。 '[1] == [1]'返回'true',但'[[1]] == [[1]]'返回'false'。謹防! – Pitarou

+4

啊,是的,有趣的一個。這實際上是因爲Swift編譯器中的一個錯誤(之前由Swift團隊確認),它不應該編譯。它不使用'Array' == ==運算符。不可以,因爲數組不是可以等同的,所以不符合我在文章中給出的定義。相反,Swift正在將數組文字隱式轉換爲指針,然後使用'UnsafePointer'的'=='。錯誤是,這不應該爲運算符啓動,只能用於函數調用(它只是在那裏調用需要使const數組更容易的C函數)。 –

+3

只是爲了正確。現在[[1]] == [[1]]返回true(Swift 2.3) – PabloR

13

==在Swift中與Java的equals()相同,它比較了值。

===在Swift中與Java的==相同,它比較了引用。

在斯威夫特,你可以那麼容易,因爲這個比較數組內容的值:

["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數組是值類型而不是引用類型。所以內存 位置是相同的只是,如果你把它比作自己(或者使用不安全的 指針)
+0

===不起作用。顯然它曾經。任何其他方式來做到這一點?另外如果你用一個非常大的數組調用函數呢?這是否會按照文檔中的說明覆制整個數組?看起來像我的開銷。 –

+1

'==='只適用於參考類型。值類型總是隻有1個引用,所以'==='對他們來說是沒有意義的。數組被「認爲」像所有結構一樣被複制,但出於性能原因,數組不會被複制,除非被突變。 – Kirsteins

+0

'myArray1 == myArray2'的原因是'NSObject'符合'Equatable',調用' - [equals:]'來進行測試。 –

4

這要看你怎麼了想要比較。例如: ["1", "2"] == ["1", "2"] // true ["1", "2"] == ["2", "1"] // false

如果你需要的是第二種情況也爲真,並確定與忽略重複的值,你可以這樣做: Set(["1", "2"]) == Set(["2", "1"]) // true (使用NSSet中的斯威夫特2)

1

陣列將符合Swift 4.1中的Equatable,否定前面答案中提到的注意事項。看起來這將在Xcode 9.3中可用。

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

但是,僅僅因爲他們實現==並不意味着ArrayOptional符合Equatable。由於這些類型可以存儲不可相等的類型,因此我們需要能夠表達它們僅在存儲可相等類型時纔可以相等。

這意味着這些==操作員有一個很大的限制:他們不能被用於兩個深層次。

有條件的一致性,我們現在可以解決這個問題。它允許我們寫出這些類型符合Equatable - 使用已定義的==運算符 - 如果它們基於的類型是可以等同的。