iOS 小册

Swift 中数组 Array 的比较

2019-06-29  本文已影响0人  fuyoufang

下面我们来尝试理解一下 Swift 中数组的比较。

var array1: [String] = ["1", "2", "3", "4", "5"]
var array2: [String] = ["1", "2", "3", "4", "5"]

// (1)  比较一下 2 个简单的数组
if array1 == array2 {
    print("equal")
} else {
    print("not equal")
}
// 输出:equal

// (2) 和一个数组的拷贝相比较
// 在 swift 中,向一个参数传值会发生拷贝
func arrayTest(anArray: [String]) -> Bool {
    return anArray == array1
}

print("Array test 1 is \(arrayTest(anArray: array1))")
print("Array test 2 is \(arrayTest(anArray: array2))")
// 两个的结果都为 true

array2.append("test")
print("Array test 2 is \(arrayTest(anArray: array2))")
// false (很明显)

array2.removeLast()
print("Array test 2 is \(arrayTest(anArray: array2))")
// true

Apple 会在结数组(结构体)需要被拷贝时进行优化,所以在赋值时,数组(结构体)有时候会被拷贝,有时候不会真的拷贝。

现在有几个问题:

  1. 在使用 == 对数组进行比较时,是迭代数组中所有的元素进行比较么?如果是这样,在比较超大的数组时,性能和内存的使用情况是怎样的?

  2. 在使用 == 进行数组比较时,如果所有的元素都相等,那数组就是相等的么?

  3. 有没有办法可以检查 array1array2 在技术上是否使用了相同的内存地址/指针。

我们来了解一下数组拷贝优化的原理是什么?它背后有什么潜在的注意事项?

看下面一段代码:

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

这个操作符循环 lhsrhs 中的所有元素,比较相同位置对应的值。它不是按位比较,而是调用每对元素的 == 操作符。这就意味着,如果给数组中的元素实现了一个自定义的 == 操作符,那这个操作符将会被调用。

但是还要考虑一种优化——如果两个数组底层的缓冲区相同,它就不会这么麻烦了,而是直接返回 true。两个数组包含相同的元素,它们当然相同了。

上面情况的困惑主要是由 NeverEqual== 操作符造成的。相等应该具备传递性,对称性和反射性。而 NeverEqual 并不具备反射性(x == x 返回 false)。这会不知不觉的让你感到困惑。

Swift 的数组为 写时拷贝。所有在写 var x == y 时,它并不会真的对数组进行拷贝,它只是将 x 的存储缓冲区的指针指向 y 的。只有在 xy 被改变时,它才会对存储缓存区进行拷贝,所以没有修改的变量不会受到影响。这就使得看起来像值类型的数组具备了较高的性能。

在早期的 Swift 版本中,可以通过 === 比较数组。

同时,在早期的版本中,修改的操作有点奇怪,如果你修改了 xy 也会随之改变,即使是用 let 进行声明的。这让人很不解,所以 Apple 对此进行了修改。

现在可以使用下面的技巧来重现 === 功能。

very implementation-dependent not to be relied-on except for poking and prodding investigations

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

a.withUnsafeBufferPointer { outer in 
    b.withUnsafeBufferPointer { inner in 
        print(inner.baseAddress == outer.baseAddress) 
    } 
}

参考

arrays-swift-comparison-compare-arrays-in-swift

上一篇 下一篇

猜你喜欢

热点阅读