Сравните массивы в Swift
Попытка понять, как 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 говорит, что за копиями массивов за сценой стоит оптимизация. Похоже, иногда - не всегда - структуры на самом деле копируются или нет.
Это сказало,
1) выполняется ли итерация по всему массиву для сравнения элементов? (похоже на это) -> Как насчет производительности / использования памяти на очень больших массивах?
2) Мы уверены, что == когда-либо вернет true, если все элементы равны? У меня плохие воспоминания о == в строках Java
3) Есть ли способ проверить, если myArray1 и myArray2 технически используют одну и ту же "ячейку памяти" / указатель / и т. Д.? Я понимаю, как работает оптимизация и возможные предупреждения.
Благодарю.
7 ответов
Вы правы немного нервничать ==
:
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
Зачем? Быстрые массивы не соответствуют Equatable
, но у них есть ==
оператор, определенный в стандартной библиотеке как:
func ==<T : Equatable>(lhs: [T], rhs: [T]) -> Bool
Этот оператор перебирает элементы в lhs
а также rhs
, сравнивая значения в каждой позиции. Это не делает побитовое сравнение - это вызывает ==
оператор на каждой паре элементов. Это означает, что если вы пишете обычай ==
для вашего элемента это будет вызываться.
Но он содержит оптимизацию - если базовые буферы для двух массивов одинаковы, это не беспокоит, оно просто возвращает true (они содержат идентичные элементы, конечно, они равны!).
Эта проблема полностью виновата NeverEqual
оператор равенства. Равенство должно быть транзитивным, симметричным и рефлексивным, а это не рефлексивным (x == x
ложно). Но это все равно может застать вас врасплох.
Swift массивы копируются при записи - поэтому, когда вы пишете var x = y
он на самом деле не делает копию массива, он просто указывает x
указатель буфера хранения на y
"S. Только если x
или же y
затем мутирует, делает ли он копию буфера, так что неизменная переменная остается неизменной. Это важно для массивов, чтобы они вели себя как типы значений, но при этом оставались производительными.
В ранних версиях Swift вы могли ===
на массивах (также в ранних версиях, поведение мутации было немного другим, если вы мутировали x
, y
также изменится, даже если он был объявлен с let
- что испугало людей, чтобы они изменили это).
Вы можете воспроизвести старое поведение ===
на массивах с этим (очень зависимым от реализации трюком, на который нельзя положиться, за исключением случаев, когда тыкаешь и подталкиваешь):
let a = [1,2,3]
var b = a
a.withUnsafeBufferPointer { outer in
b.withUnsafeBufferPointer { inner in
println(inner.baseAddress == outer.baseAddress)
}
}
==
в Swift такой же, как в Java equals()
, он сравнивает значения.
===
в Swift такой же, как в Java ==
Сравнивает ссылки.
В Swift вы можете сравнивать значения содержимого массива так же просто, как это:
["1", "2"] == ["1", "2"]
Но это не сработает, если вы хотите сравнить ссылки:
var myArray1 = [NSString(string: "1")]
var myArray2 = [NSString(string: "1")]
myArray1[0] === myArray2[0] // false
myArray1[0] == myArray2[0] // true
Итак, ответы:
- Я думаю, что производительность является оптимальной для сравнения значений (не референс)
- Да, если вы хотите сравнить значения
- Массивы Swift являются типом значения, а не ссылочным типом. Таким образом, область памяти одинакова, только если вы сравниваете ее с собой (или используете небезопасные указатели)
Это зависит от того, как вы хотите сравнить. Например:
["1", "2"] == ["1", "2"] // true
но
["1", "2"] == ["2", "1"] // false
Если вам нужно, чтобы второй случай также был истинным и можно игнорировать повторяющиеся значения, вы можете сделать следующее:
Set(["1", "2"]) == Set(["2", "1"]) // true
(используйте NSSet для Swift 2)
Для сравнения массивов пользовательских объектов мы можем использовать 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")
}
Массивы соответствуют Equatable
в Swift 4.1 отрицание предостережений, упомянутых в предыдущих ответах. Это доступно в Xcode 9.3.
https://swift.org/blog/conditional-conformance/
Но только потому, что они реализовали
==
не имел ввидуArray
или жеOptional
соответствуетEquatable
, Поскольку эти типы могут хранить несовместимые типы, нам нужно было иметь возможность выразить, что они совместимы только при хранении сопоставимых типов.Это означало, что это
==
у операторов было большое ограничение: их нельзя использовать на двух уровнях.Теперь мы можем это исправить с помощью условного соответствия. Это позволяет нам писать, что эти типы соответствуют
Equatable
- используя уже определенные==
оператор - если типы, на которых они основаны, равносильны.
При сравнении массивов в swift должно быть ясно, что такое объекты сравнения.
Бывший:
A = [1,2,3]
B = [2,1,3]
return A == B -> false
Сравнение выполняется путем обхода обоих массивов и сравнения данных его индекса.return Set(A) == Set(B) -> true
Благодаря функции Set() сравнение производится по целой коллекции единичных значений.
«Операции над наборами не ограничиваются использованием с другими наборами. Вместо этого вы можете выполнять операции над наборами с другим набором, массивом или любым другим типом последовательности».
Используйте оператор «равно» (==), чтобы проверить, содержат ли два набора > одни и те же элементы. https://developer.apple.com/documentation/swift/set
Подробнее можно найти здесь:https://docs.swift.org/swift-book/LanguageGuide/CollectionTypes.html
Если у вас есть массив пользовательских объектов, нужно быть осторожным с тестом на равенство, по крайней мере, с Swift 4.1:
Если пользовательский объект не является подклассом NSObject
сравнение использует static func == (lhs: Nsobject, rhs: Nsobject) -> Bool
, который должен быть определен.
Если это подкласс NSObject
, он использует func isEqual(_ object: Any?) -> Bool
, который должен быть переопределен.
Пожалуйста, проверьте следующий код и установите точки останова во всех операторах возврата.
class Object: Equatable {
static func == (lhs: Object, rhs: Object) -> Bool {
return true
}
}
Следующий класс наследует Equatable
от NSObject
class Nsobject: NSObject {
static func == (lhs: Nsobject, rhs: Nsobject) -> Bool {
return true
}
override func isEqual(_ object: Any?) -> Bool {
return true
}
}
Их можно сравнить с:
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