2014-12-19 18 views
38

Versuchen zu verstehen, wie schnell Arrays vergleicht.Vergleichen Sie Arrays in 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 sagt, es gibt Optimierungen hinter der Szene auf Array-Kopien. Sieht aus wie manchmal - nicht immer - Strukturen werden tatsächlich kopiert oder nicht.

Das heißt,

1) == alle Array-Iteration über einen elementbasierten Vergleich durchzuführen? (sieht so aus) -> Wie wäre es dann mit Leistung/Speicherverbrauch auf sehr großen Arrays?

2) Sind wir sicher == wird jemals wahr zurückkehren, wenn alle Elemente gleich sind? Ich habe schlechte Erinnerungen an == Java Strings

3) Gibt es eine Möglichkeit zu überprüfen, ob myArray1 und myArray2 technisch die gleiche "Speicherort"/Zeiger/etc. verwenden? Ich bin nach dem Verständnis, wie die Optimierung funktioniert und mögliche Vorbehalte.

Danke.

+0

Direkter Zeigervergleich ist '===' – Anorak

+0

Funktioniert nicht. === sagt -> [String] entspricht nicht AnyObject –

+0

@Anorak '===' ist nur für Klassen, 'Array' ist eine Struktur. – Kirsteins

Antwort

58

Du hast Recht, daß sie etwas nervös über ==:

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 

Warum? Swift-Arrays entsprechen nicht Equatable, aber sie haben einen == Operator, in der Standardbibliothek definiert als:

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

Dieser Operator Schlaufen über die Elemente in lhsrhs und an jeder Position die Werte vergleicht. Es tut nicht einen bitweisen Vergleich - es ruft die ==-Operator für jedes Paar Elemente. Das heißt, wenn Sie eine benutzerdefinierte == für Ihr Element schreiben, wird es aufgerufen.

Aber es enthält eine Optimierung - wenn die zugrunde liegenden Puffer für die beiden Arrays die gleichen sind, stört es nicht, es gibt nur wahr (sie enthalten identische Elemente, natürlich sind sie gleich!).

Dieses Problem ist ausschließlich der Fehler NeverEqual Gleichheitsoperator. Gleichheit sollte transitiv, symmetrisch und reflexiv sein, und diese ist nicht reflexiv (x == x ist falsch). Aber es könnte dich immer noch überraschen.

Swift Arrays copy-on-write sind - so, wenn Sie var x = y schreiben es eigentlich keine Kopie des Arrays machen, es zeigt nur x ‚s Speicherpuffer Zeiger auf y‚s. Nur wenn x oder y später mutiert werden, erstellt es dann eine Kopie des Puffers, so dass die unveränderte Variable nicht betroffen ist. Dies ist wichtig, damit Arrays sich wie Werttypen verhalten, aber immer noch leistungsfähig sind.

In früheren Versionen von Swift, man könnte tatsächlich === auf Arrays nennen (auch in früheren Versionen war das Mutieren Verhalten ein bisschen anders, wenn Sie x mutiert, y auch noch ändern würde, wenn es mit let erklärt worden war - das Leute ausgeflippt, also änderten sie es).

Sie können irgendwie das alte Verhalten von === auf Arrays mit diesem (sehr abhängig von der Implementierung nicht mit Ausnahme von Stossen und Druck Untersuchungen verlassen-on wird) reproduzieren Trick:

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

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

Da steckt mehr dahinter, als man sieht. '[1] == [1]' gibt 'true' zurück, aber' [[1]] == [[1]] 'gibt' false' zurück. In acht nehmen! – Pitarou

+4

Ah ja, Spaß, dass. Es liegt an einem Fehler im Swift-Compiler (der zuvor vom Swift-Team bestätigt wurde) und sollte nicht kompiliert werden. Es wird nicht der 'Array'' == 'Operator verwendet. Kann nicht sein, weil Arrays nicht gleichbar sind und nicht mit der Definition übereinstimmen, die ich in meinem Beitrag angegeben habe. Statt dessen macht Swift eine implizite Umwandlung der Array-Literale in einen Zeiger, und dann wird das '==' für 'UnsafePointer' verwendet. Der Fehler ist, das sollte nicht für Operatoren, nur für Funktionsaufrufe (es ist nur da, um den Aufruf von C-Funktionen, die ein const-Array benötigen, einfacher) zu treten. –

+3

Nur um richtig zu sein. Jetzt [[1]] == [[1]] gibt wahr zurück (Swift 2.3) – PabloR

13

== in Swift ist das gleiche wie Javas equals(), es vergleicht Werte.

=== in Swift ist das gleiche wie Javas ==, es vergleicht Referenzen.

In Swift Sie Array Inhalt Werte so einfach wie diese vergleichen kann:

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

Aber das wird nicht funktionieren, wenn Sie Referenzen vergleichen wollen:

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

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

So sind die Antworten:

  1. Ich denke, die Leistung ist optimal für den Wert (nicht Referenz) Vergleiche
  2. Ja, wenn Sie Werte vergleichen möchten
  3. Swift-Arrays sind Werttyp und nicht Referenztyp. So ist der Speicher Lage ist das gleiche nur, wenn Sie es selbst zu vergleichen (oder verwenden unsichere Zeiger)
+0

=== funktioniert nicht. Anscheinend war es früher so. Irgendeine andere Möglichkeit, es zu tun? Und was ist, wenn Sie eine Funktion mit einem sehr großen Array aufrufen? Wird das ganze Array wie in der Dokumentation beschrieben kopiert? Sieht für mich wie ein Overhead aus. –

+1

'===' funktioniert nur für Referenztypen. Werttypen haben immer nur einen Bezug auf sie, also ist '===' für sie nicht sinnvoll. Arrays werden "als" betrachtet, um wie alle Strukturen kopiert zu werden, aber aus Gründen der Leistungsfähigkeit werden Arrays nicht kopiert, es sei denn, sie werden mutiert. – Kirsteins

+0

Der Grund 'myArray1 == myArray2' ist, dass' NSObject' mit 'Equatable' konform ist und' - [equals:] 'aufruft, um den Test durchzuführen. –

4

Es hängt davon ab, wie wollen Sie möchte vergleichen. Zum Beispiel: ["1", "2"] == ["1", "2"] // true aber ["1", "2"] == ["2", "1"] // false

Wenn Sie das zweite Fall müssen auch wahr sein, und sind in Ordnung mit sich wiederholenden Werte zu ignorieren, können Sie tun: Set(["1", "2"]) == Set(["2", "1"]) // true (verwenden NSSet für Swift 2)

1

Arrays wird in Swift 4.1 Equatable entsprechen und die in früheren Antworten erwähnten Einschränkungen negieren. Es scheint, dass dies in Xcode 9.3 verfügbar sein wird.

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

Aber nur, weil sie == umgesetzt bedeutete nicht Array oder Optional zu Equatable angepasst. Da diese Typen nichtgleichbare Typen speichern können, mussten wir ausdrücken können, dass sie nur dann gleichsetzbar sind, wenn ein gleichsetzbarer Typ gespeichert wird.

Dies bedeutete, dass diese == Betreiber eine große Einschränkung hatten: Sie konnten nicht zwei Ebenen tief verwendet werden.

Mit bedingter Konformität können wir dies jetzt beheben. Es erlaubt uns zu schreiben, dass diese Typen Equatable - unter Verwendung des bereits definierten ==-Operators - entsprechen, wenn die Typen, auf denen sie basieren, gleich sind.