2014-12-19 31 views
38

Cercando di capire in che modo si confrontano rapidamente gli array.Confronto di array 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 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,

1) è == iterazione di tutto l'array di eseguire un confronto elemento basato? (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) C'è 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.

+0

Il confronto diretto del puntatore è '===' – Anorak

+0

Non funziona. === dice -> [String] non è conforme a AnyObject –

+0

@Anorak '===' è solo per le classi, 'Array' è una struttura. – Kirsteins

risposta

58

Hai ragione di essere un po 'nervoso per ==:

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é? array Swift non sono conformi al Equatable, ma hanno un == operatore, definita nella libreria standard come:

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

Questo operatore passanti sugli elementi in lhs e rhs, confrontando i valori in ciascuna posizione. Fa non fare un confronto bit a bit - chiama l'operatore == su ogni coppia di elementi. Ciò significa che se scrivi un elemento personalizzato == per il tuo elemento, verrà chiamato.

Ma contiene un'ottimizzazione - se i buffer sottostanti per i due array sono uguali, non si preoccupa, restituisce semplicemente true (contengono elementi identici, ovviamente sono uguali!).

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

array Swift sono copy-on-write - in modo che quando si scrive var x = y in realtà non fanno una copia della matrice, ma solo punti x ‘puntatore del buffer di memorizzazione s in y‘s. Solo se x o vengono mutati in seguito, viene creata 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 potrebbe chiamare === su array (anche nelle prime versioni, il comportamento mutazione era un po 'diverso, se mutati x, y sarebbe anche cambiare, anche se fosse stato dichiarato con let - che hanno spaventato le persone e l'hanno cambiato).

Puoi po riprodurre il vecchio comportamento di === su array con questo (molto implementazione-dipendente non per poter essere invocata-on ad eccezione di frugando e incitamento indagini) trucco:

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

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

C'è di più in questo rispetto all'occhio. '[1] == [1]' restituisce 'true', ma' [[1]] == [[1]] 'restituisce' false'. Attenzione! – Pitarou

+4

Ah sì, divertente quello. In realtà a causa di un bug nel compilatore Swift (confermato precedentemente dal team Swift), e non dovrebbe essere compilato. Non sta usando l'operatore 'Array' ==='. Non può essere, perché gli array non sono equi e quindi non corrispondono alla definizione che ho dato nel mio post. Invece, quello che sta succedendo è che Swift sta facendo una conversione implicita dei letterali della matrice a un puntatore, e quindi viene usato '==' per 'UnsafePointer'. Il bug è che questo non dovrebbe dare il via agli operatori, solo per le chiamate di funzione (è solo lì per rendere più facile chiamare le funzioni C che richiedono un array const). –

+3

Solo per il gusto di essere corretto. Ora [[1]] == [[1]] restituisce true (Swift 2.3) – PabloR

13

== in Swift è lo stesso di Java equals(), confronta i valori.

=== in Swift è lo stesso di Java ==, confronta i riferimenti.

In Swift è possibile confrontare i valori contenuti serie facile come questo:

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

Ma questo non funzionerà se si desidera 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 valore (non riferimento) confronti
  2. Sì, se si desidera confrontare i valori
  3. Gli array Swift sono di tipo valore e non di riferimento. Quindi la posizione di memoria è la stessa solo se lo si confronta con se stesso (o utilizzare non sicuri puntatori)
+0

=== non funziona. Apparentemente era abituato. Qualche altro modo per farlo? Inoltre, se si chiama una funzione con un array molto grande? Copierà l'intero array come indicato nella documentazione? Sembra un sovraccarico per me. –

+1

'===' funzionerà solo con i tipi di riferimento. I tipi di valore hanno sempre solo un riferimento a loro quindi '===' non avrebbe senso per loro. Gli array sono "considerati" per essere copiati come tutte le strutture, ma per motivi di prestazioni sotto gli array di cappe non vengono copiati a meno che non vengano mutati. – Kirsteins

+0

Il motivo 'myArray1 == myArray2' è che' NSObject' è conforme a 'Equatable', chiamando' - [equals:] 'per condurre il test. –

4

Dipende da come si fa voglio confrontare. Per esempio: ["1", "2"] == ["1", "2"] // true ma ["1", "2"] == ["2", "1"] // false

Se è necessario che secondo caso ad essere anche vero e sono ok con ignorando i valori ripetitivi, si può fare: Set(["1", "2"]) == Set(["2", "1"]) // true (utilizzare NSSet per Swift 2)

1

Array sarà conforme a Equatable in Swift 4.1, annullando le avvertenze citate nelle risposte precedenti. Sembra che questo sarà disponibile in Xcode 9.3.

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

Ma solo perché hanno implementato == non significava Array o Optional conformare 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à condizionale, 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.