Comparer les tableaux dans swift

Essayer de comprendre comment swift compare les tableaux.

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 dit qu'il y a des optimisations derrière la scène sur les copies de tableaux. On dirait que parfois-pas toujours-les structures sont réellement copiées ou non.

Cela dit,

1) est-ce que == itère sur tout le tableau pour effectuer une comparaison basée sur des éléments ? (qui ressemble à ça) - >Que diriez-vous des performances / utilisation de la mémoire sur de très grands tableaux alors ?

2) sommes-nous sûrs que = = retournera jamais true si tous les éléments sont égaux ? Je avoir de mauvais souvenirs de = = sur les chaînes Java

3) Existe-t-il un moyen de vérifier si myArray1 et myArray2 utilisent techniquement le même" emplacement mémoire " / pointeur / etc. ? je suis après avoir compris comment fonctionne l'optimisation et les mises en garde potentielles.

Merci.

46
demandé sur vivien.destpern 2014-12-19 17:06:59

6 réponses

Vous avez raison d'être un peu nerveux à propos de ==:

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

Pourquoi? Les tableaux Swift ne sont pas conformes à Equatable, mais ils ont un opérateur ==, défini dans la bibliothèque standard comme:

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

Cet opérateur boucle sur les éléments de lhs et rhs, en comparant les valeurs à chaque position. Il fait pas faire une comparaison au niveau du BIT - il appelle l'opérateur == sur chaque paire d'éléments. Cela signifie que si vous écrivez un == personnalisé pour votre élément, il obtiendra appelé.

Mais il contient une optimisation-si les tampons sous-jacents pour les deux tableaux sont les mêmes, ça ne dérange pas, ça renvoie juste true (ils contiennent des éléments identiques, bien sûr ils sont égaux!).

Ce problème est entièrement la faute de l'opérateur d'égalité NeverEqual. L'égalité devrait être transitive, symétrique et réflexive, et celle - ci n'est pas réflexive (x == x est fausse). Mais il pourrait encore vous attraper au dépourvu.

Swift tableaux sont de copie sur écriture – ainsi, lorsque vous write var x = y Il ne fait pas réellement une copie du tableau, il pointe juste le pointeur du tampon de stockage de x à y. seulement si x ou y sont mutés plus tard, il fait une copie du tampon, de sorte que la variable inchangée ne soit pas affectée. Ceci est essentiel pour que les tableaux se comportent comme des types de valeur mais soient toujours performants.

Dans les premières versions de Swift, vous pouvez réellement appeler === sur les tableaux (également dans les premières versions, le comportement de mutation était un peu différent, si vous avez muté x, y changerait aussi même s'il avait été déclaré Avec let – ce qui a effrayé les gens alors ils l'ont changé).

Vous pouvez reproduire un peu l'ancien comportement de === sur les tableaux avec ce truc (très dépendant de l'implémentation à ne pas compter sauf pour les enquêtes de piquage et de poussée):

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

a.withUnsafeBufferPointer { outer in 
    b.withUnsafeBufferPointer { inner in 
        println(inner.baseAddress == outer.baseAddress) 
    } 
}
63
répondu Airspeed Velocity 2016-12-29 00:30:23

== dans Swift est le même que equals() de Java, il compare les valeurs.

=== dans Swift est le même que == de Java, il compare les références.

Dans Swift, vous pouvez comparer les valeurs de contenu de tableau aussi facilement que ceci:

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

, Mais cela ne fonctionnera pas si vous voulez comparer les références:

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

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

, Donc les réponses:

  1. je pense que la performance est optimale pour faire de la valeur (pas de référence) comparaisons
  2. Oui, si vous voulez comparer les valeurs
  3. Swift les tableaux sont de type valeur et non de type référence. De sorte que la mémoire l'emplacement est le même que si vous le comparer à lui-même (ou insalubres les pointeurs)
16
répondu Kirsteins 2014-12-19 14:50:07

Cela dépend de la façon dont vous voulez comparer. Exemple: ["1", "2"] == ["1", "2"] // true mais ["1", "2"] == ["2", "1"] // false

Si vous avez besoin que ce deuxième cas soit également vrai et que vous ignorez les valeurs répétitives, vous pouvez faire: Set(["1", "2"]) == Set(["2", "1"]) // true (utiliser NSSet pour Swift 2)

6
répondu demosten 2017-01-27 17:13:25

Les tableaux sont conformes à Equatable dans Swift 4.1, annulant les mises en garde mentionnées dans les réponses précédentes. Ceci est disponible dans Xcode 9.3.

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

Mais juste parce qu'ils ont implémenté == ne signifiait pas Array ou Optional conforme à Equatable. Étant donné que ces types peuvent stocker des types Non équatables, nous devions être en mesure d'exprimer qu'ils ne sont équatables que lors du stockage d'un type équatable.

Cela signifiait ces opérateurs == avait une grande limitation: ils ne pouvaient pas être utilisés à deux niveaux de profondeur.

Avec la conformité conditionnelle, nous pouvons maintenant résoudre ce problème. Cela nous permet d'écrire que ces types sont conformes à Equatable-en utilisant l'opérateur == déjà défini-si les types sur lesquels ils sont basés sont équivalents.

4
répondu Greg McCoy 2018-04-09 18:14:30

Pour comparer des tableaux d'objets personnalisés, nous pouvons utiliser 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")
}
1
répondu tier777 2018-06-28 22:28:34

Si vous avez un matrice de objets personnalisés, on doit être prudent avec le test d'égalité, au moins avec Swift 4.1:
Si l'objet personnalisé est pas une sous-classe de NSObject, la comparaison utilise le static func == (lhs: Nsobject, rhs: Nsobject) -> Bool, ce qui doit être définie.
Si il est une sous-classe de NSObject, il utilise le func isEqual(_ object: Any?) -> Bool, qui doit être remplacée.

Veuillez vérifier le code suivant et définir des points d'arrêt à toutes les instructions de retour.

class Object: Equatable {

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

La classe suivante hérite Equatable de NSObject

class Nsobject: NSObject {

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


    override func isEqual(_ object: Any?) -> Bool {
        return true
    }

}  

Ils peuvent être comparés avec:

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
1
répondu Reinhard Männer 2018-07-15 14:41:37