Сравнение массивов в быстрых

Попытка понять, как быстро сравнивает массивы.

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) Мы уверены, что == когда-нибудь вернется, если все элементы равны? У меня плохие воспоминания о == на Java Strings

3) Есть ли способ проверить, являются ли myArray1 и myArray2 технически одним и тем же "местом памяти" /указателем/и т.д.? Я понимаю, как работает оптимизация и потенциальные оговорки.

Спасибо.

Ответ 1

Вы можете слегка нервничать о ==:

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

Почему? Массивы Swift не соответствуют Equatable, но они имеют оператор ==, определенный в стандартной библиотеке как:

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

Этот оператор пересекает элементы в lhs и rhs, сравнивая значения в каждой позиции. Он не выполняет побитовое сравнение - он вызывает оператор == для каждой пары элементов. Это означает, что если вы напишете пользовательский == для своего элемента, он будет вызван.

Но он содержит оптимизацию - если базовые буферы для двух массивов одинаковы, он не беспокоится, он просто возвращает true (они содержат одинаковые элементы, конечно же, равны!).

Эта проблема полностью является ошибкой оператора равенства NeverEqual. Равенство должно быть транзитивным, симметричным и рефлексивным, а это не рефлексивным (x == x является ложным). Но он все равно мог бы застать вас врасплох.

Быстрые массивы копируются при записи - поэтому, когда вы пишете var x = y, он фактически не делает копию массива, он просто указывает указатель буфера хранения x s на 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) 
    } 
}

Ответ 2

== в 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 - это тип значения, а не ссылочный тип. Итак, память местоположение будет таким же, только если вы сравните его с собой (или используйте небезопасные указатели)

Ответ 3

Это зависит от того, как вы хотите сравнить. Например: ["1", "2"] == ["1", "2"] // true но ["1", "2"] == ["2", "1"] // false

Если вам нужен этот второй случай, чтобы он был истинным, и вы можете игнорировать повторяющиеся значения, вы можете сделать: Set(["1", "2"]) == Set(["2", "1"]) // true (используйте NSSet для Swift 2)