Confronta gli array in rapido

Cercando di capire quanto sia veloce la comparazione degli array.

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 dice che ci sono ottimizzazioni dietro la scena su copie di array. Sembra che a volte – non sempre – le strutture siano effettivamente copiate o meno.

Detto ciò,

1) == iterating su tutta la matrice per eseguire un confronto basato sugli elementi? (sembra) -> Che ne dici di prestazioni / utilizzo della memoria su array molto grandi allora?

2) Siamo sicuri che == restituirà mai true se tutti gli elementi sono uguali? Ho brutti ricordi di == su Java Strings

3) Esiste un modo per verificare se myArray1 e myArray2 utilizzano tecnicamente la stessa “posizione di memoria” / puntatore / ecc.? Ho capito come funziona l’ottimizzazione e i potenziali avvertimenti.

Grazie.

Hai ragione ad essere un po ‘nervoso riguardo == :

 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 

Perché? Gli array Swift non sono conformi a Equatable , ma hanno un operatore == , definito nella libreria standard come:

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

Questo operatore scorre gli elementi in lhs e rhs , confrontando i valori in ogni posizione. Non fa un confronto bit a bit – chiama l’operatore == su ogni coppia di elementi. Ciò significa che se scrivi un == personalizzato per il tuo elemento, verrà chiamato.

Ma contiene un’ottimizzazione – se i buffer sottostanti per i due array sono uguali, non infastidisce, restituisce semplicemente true (contengono elementi identici, ovviamente sono uguali!).

Questo problema è interamente colpa NeverEqual uguaglianza di NeverEqual . L’uguaglianza dovrebbe essere transitiva, simmetrica e riflessiva, e questa non è riflessiva ( x == x è falso). Ma potrebbe ancora sorprenderti.

Gli array Swift sono copy-on-write, quindi quando scrivi var x = y in realtà non fa una copia dell’array, punta solo x il puntatore del buffer di memoria su y . Solo se x o y sono mutati in seguito, crea una copia del buffer, in modo che la variabile invariata non subisca modifiche. Questo è fondamentale per gli array per comportarsi come tipi di valore, ma essere comunque performanti.

Nelle prime versioni di Swift, in realtà si poteva chiamare === sugli array (anche nelle prime versioni, il comportamento mutante era un po ‘diverso, se si mutava x , si cambierebbe anche se fosse stato dichiarato con let – quali persone spaventate fuori così hanno cambiato).

Puoi kinda riprodurre il vecchio comportamento di === sugli array con questo trucco (molto dipendente dall’implementazione di non essere affidabile, eccetto per le indagini su poking e prodding):

 let a = [1,2,3] var b = a a.withUnsafeBufferPointer { outer in b.withUnsafeBufferPointer { inner in println(inner.baseAddress == outer.baseAddress) } } 

== in Swift è uguale a Java’s equals() , confronta i valori.

=== in Swift è uguale a Java == , confronta i riferimenti.

In Swift puoi confrontare i valori del contenuto dell’array in modo semplice:

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

Ma questo non funzionerà se vuoi confrontare i riferimenti:

 var myArray1 = [NSString(string: "1")] var myArray2 = [NSString(string: "1")] myArray1[0] === myArray2[0] // false myArray1[0] == myArray2[0] // true 

Quindi le risposte:

  1. Penso che le prestazioni siano ottimali per fare confronti di valore (non di riferimento)
  2. Sì, se vuoi confrontare i valori
  3. Gli array Swift sono di tipo valore e non di riferimento. Quindi la posizione della memoria è la stessa solo se la confronti con se stessa (o usa puntatori non sicuri)

Dipende da come vuoi confrontarti. Ad esempio: ["1", "2"] == ["1", "2"] // true ma ["1", "2"] == ["2", "1"] // false

Se hai bisogno che anche il secondo caso sia vero e stai bene ignorando i valori ripetitivi, puoi fare: Set(["1", "2"]) == Set(["2", "1"]) // true (usa NSSet per Swift 2)

Le matrici sono conformi a Equatable in Swift 4.1, annullando le avvertenze citate nelle risposte precedenti. Questo è disponibile in Xcode 9.3.

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

Ma solo perché implementavano == non significava Array o Optional conforms a Equatable . Dal momento che questi tipi possono memorizzare tipi non equivalenti, dovevamo essere in grado di esprimere che sono equivalenti solo quando si memorizza un tipo equabile.

Ciò significava che questi operatori == avevano un grosso limite: non potevano essere usati due livelli in profondità.

Con la conformità condizionata, ora possiamo risolvere questo problema. Ci consente di scrivere che questi tipi sono conformi a Equatable -utilizzando l’operatore == già definito-se i tipi su cui si basano sono equi

Se hai una serie di oggetti personalizzati , devi fare attenzione con il test di uguaglianza, almeno con Swift 4.1:
Se l’object personalizzato non è una sottoclass di NSObject , il confronto utilizza la static func == (lhs: Nsobject, rhs: Nsobject) -> Bool , che deve essere definito.
Se è una sottoclass di NSObject , usa il func isEqual(_ object: Any?) -> Bool , che deve essere sovrascritto.

Si prega di controllare il seguente codice e impostare i breakpoint su tutte le dichiarazioni di reso.

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

La seguente class eredita Equatable da NSObject

 class Nsobject: NSObject { static func == (lhs: Nsobject, rhs: Nsobject) -> Bool { return true } override func isEqual(_ object: Any?) -> Bool { return true } } 

Possono essere confrontati con:

 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 

Per confrontare gli array di oggetti personalizzati, possiamo usare 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") }