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:
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 significavaArray
oOptional
conforms aEquatable
. 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") }