Compara arrays en swift
comparison (6)
Depende de cómo quieras comparar. Por ejemplo: ["1", "2"] == ["1", "2"] // true
pero ["1", "2"] == ["2", "1"] // false
Si necesita que el segundo caso también sea cierto y está de acuerdo con ignorar los valores repetitivos, puede hacer: Set(["1", "2"]) == Set(["2", "1"]) // true
(usa NSSet para Swift 2)
Tratando de entender cómo Swift compara arrays.
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 que hay optimizaciones detrás de la escena en las copias de matriz. Parece que a veces, no siempre, las estructuras se copian o no.
Dicho esto,
1) ¿Es == iterar sobre toda la matriz para realizar una comparación basada en elementos? (parece que sí) -> ¿Qué hay del rendimiento / uso de memoria en arreglos muy grandes, entonces?
2) ¿Estamos seguros de que == alguna vez devolverá verdadero si todos los elementos son iguales? Tengo malos recuerdos de == en Java Strings
3) ¿Hay alguna forma de verificar si myArray1 y myArray2 están utilizando técnicamente la misma "ubicación de memoria" / puntero / etc.? Estoy después de entender cómo funciona la optimización y posibles advertencias.
Gracias.
Las matrices se ajustan a Equatable
en Swift 4.1, anulando las advertencias mencionadas en las respuestas anteriores. Esto está disponible en Xcode 9.3.
https://swift.org/blog/conditional-conformance/
Pero solo porque implementaron
==
no significaba queArray
uOptional
ajustaban aEquatable
. Dado que estos tipos pueden almacenar tipos no comparables, necesitábamos poder expresar que solo son comparables cuando se almacena un tipo compatible.Esto significaba que estos
==
operadores tenían una gran limitación: no podían usarse a dos niveles de profundidad.Con la conformidad condicional, ahora podemos arreglar esto. Nos permite escribir que estos tipos se ajustan a
Equatable
utilizando el operador==
ya definido, si los tipos en los que se basan son comparables.
Para comparar arrays de objetos personalizados podemos usar 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")
}
Si tiene una matriz de objetos personalizados , uno debe tener cuidado con la prueba de igualdad, al menos con Swift 4.1:
Si el objeto personalizado no es una subclase de NSObject
, la comparación utiliza la static func == (lhs: Nsobject, rhs: Nsobject) -> Bool
, que debe definirse.
Si es una subclase de NSObject
, utiliza la func isEqual(_ object: Any?) -> Bool
, que debe ser anulada.
Verifique el siguiente código y establezca puntos de interrupción en todas las declaraciones de devolución.
class Object: Equatable {
static func == (lhs: Object, rhs: Object) -> Bool {
return true
}
}
La siguiente clase Equatable
de NSObject
class Nsobject: NSObject {
static func == (lhs: Nsobject, rhs: Nsobject) -> Bool {
return true
}
override func isEqual(_ object: Any?) -> Bool {
return true
}
}
Se pueden comparar 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
Tienes razón para estar un poco nervioso por ==
:
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
¿Por qué? Los arreglos Swift no se ajustan a Equatable
, pero tienen un operador ==
, definido en la biblioteca estándar como:
func ==<T : Equatable>(lhs: [T], rhs: [T]) -> Bool
Este operador recorre los elementos en lhs
y rhs
, comparando los valores en cada posición. No hace una comparación a nivel de bits: llama al operador ==
en cada par de elementos. Eso significa que si escribes un ==
personalizado para tu elemento, se llamará.
Pero contiene una optimización: si los buffers subyacentes para los dos arreglos son los mismos, no molesta, simplemente devuelve verdadero (contienen elementos idénticos, ¡por supuesto que son iguales!).
Este problema es totalmente culpa del operador de igualdad NeverEqual
. La igualdad debe ser transitiva, simétrica y reflexiva, y esta no es reflexiva ( x == x
es falso). Pero todavía podría atraparte desprevenido.
Los arreglos Swift son de copia en escritura, por lo que cuando escribe var x = y
en realidad no hace una copia del arreglo, solo apunta el puntero del búfer de almacenamiento de x
a y
. Solo si x
o y
se mutan más tarde, se hace una copia del búfer, de modo que la variable no modificada no se vea afectada. Esto es crítico para que las matrices se comporten como tipos de valor pero aún así sean eficaces.
En las primeras versiones de Swift, realmente podías llamar ===
a los arreglos (también en las primeras versiones, el comportamiento de mutación era un poco diferente, si mutabas x
, y
también cambiaría a pesar de que se había declarado con let
, lo que asustaba a la gente fuera así que lo cambiaron).
Puede reproducir un poco el viejo comportamiento de ===
en las matrices con este truco (muy dependiente de la implementación en el que no se puede confiar, excepto para investigar las investigaciones y el empuje):
let a = [1,2,3]
var b = a
a.withUnsafeBufferPointer { outer in
b.withUnsafeBufferPointer { inner in
println(inner.baseAddress == outer.baseAddress)
}
}
==
en Swift es lo mismo que los equals()
de Java equals()
, compara valores.
===
en Swift es el mismo que en Java ==
, compara referencias.
En Swift puedes comparar valores de contenido de matriz tan fácil como esto:
["1", "2"] == ["1", "2"]
Pero esto no funcionará si desea comparar referencias:
var myArray1 = [NSString(string: "1")]
var myArray2 = [NSString(string: "1")]
myArray1[0] === myArray2[0] // false
myArray1[0] == myArray2[0] // true
Así que las respuestas:
- Creo que el rendimiento es óptimo para hacer comparaciones de valor (no de referencia)
- Sí, si quieres comparar valores.
- Los arreglos Swift son tipo de valor y no tipo de referencia. Por lo tanto, la ubicación de la memoria es la misma solo si la compara con ella misma (o usa punteros inseguros)