【发布时间】:2015-04-12 09:56:57
【问题描述】:
在 Swift 中是否有一个函数可以检查数组的所有元素是否具有相同的值?在我的例子中,它是一个Int 类型的数组。我知道我可以使用一个简单的 for 循环对其进行迭代我只是想知道是否有内置的更快的东西。
【问题讨论】:
在 Swift 中是否有一个函数可以检查数组的所有元素是否具有相同的值?在我的例子中,它是一个Int 类型的数组。我知道我可以使用一个简单的 for 循环对其进行迭代我只是想知道是否有内置的更快的东西。
【问题讨论】:
使用 Swift 5,您可以使用以下四种方法之一来测试数组的所有元素是否相等。
Array的allSatisfy(_:)方法allSatisfy(_:) 返回一个布尔值,指示序列的每个元素是否满足给定的谓词。您可以设置谓词来测试数组的所有元素是否相等:
let array = [1, 1, 1]
let hasAllItemsEqual = array.dropFirst().allSatisfy({ $0 == array.first })
print(hasAllItemsEqual) // prints: true
let array = [1, 1, 3]
let hasAllItemsEqual = array.dropFirst().allSatisfy({ $0 == array.first })
print(hasAllItemsEqual) // prints: false
let array = [Int]()
let hasAllItemsEqual = array.dropFirst().allSatisfy({ $0 == array.first })
print(hasAllItemsEqual) // prints: true
Array的reduce(_:_:)方法作为allSatisfy(_:) 的替代品,您可以使用reduce(_:_:):
let array = [1, 1, 1]
let hasAllItemsEqual = array.dropFirst().reduce(true) { (partialResult, element) in
return partialResult && element == array.first
}
print(hasAllItemsEqual) // prints: true
let array = [1, 1, 3]
let hasAllItemsEqual = array.dropFirst().reduce(true) { (partialResult, element) in
return partialResult && element == array.first
}
print(hasAllItemsEqual) // prints: false
let array = [Int]()
let hasAllItemsEqual = array.dropFirst().reduce(true) { (partialResult, element) in
return partialResult && element == array.first
}
print(hasAllItemsEqual) // prints: true
elementsEqual(_:)方法elementsEqual(_:) 返回一个布尔值,指示两个序列是否包含相同顺序的相同元素。因此,您可以通过重复初始数组的第一个元素并将前者与后者进行比较来创建一个新集合:
let array = [1, 1, 1]
precondition(!array.isEmpty)
let repeated = repeatElement(array[0], count: array.count)
let hasAllItemsEqual = array.elementsEqual(repeated)
print(hasAllItemsEqual) // prints: true
let array = [1, 1, 3]
precondition(!array.isEmpty)
let repeated = repeatElement(array[0], count: array.count)
let hasAllItemsEqual = array.elementsEqual(repeated)
print(hasAllItemsEqual) // prints: false
Set 的 init(_:) 初始化器如果一个数组的所有元素都是相等的,从这个数组创建一个集合应该导致这个集合只有一个元素:
let array = [1, 1, 1]
let set = Set(array)
let hasAllItemsEqual = set.count <= 1
print(hasAllItemsEqual) // prints: true
let array = [1, 1, 3]
let set = Set(array)
let hasAllItemsEqual = set.count <= 1
print(hasAllItemsEqual) // prints: false
let array = [Int]()
let set = Set(array)
let hasAllItemsEqual = set.count <= 1
print(hasAllItemsEqual) // prints: true
【讨论】:
任何方法都必须遍历所有元素,直到找到不同的元素:
func allEqualUsingLoop<T : Equatable>(array : [T]) -> Bool {
if let firstElem = array.first {
for elem in array {
if elem != firstElem {
return false
}
}
}
return true
}
您可以使用contains() 函数代替显式循环:
func allEqualUsingContains<T : Equatable>(array : [T]) -> Bool {
if let firstElem = array.first {
return !contains(array, { $0 != firstElem })
}
return true
}
如果数组元素是Hashable(比如Int)那么你可以
从数组元素创建一个Set(从 Swift 1.2 开始可用)并检查它是否只有一个元素。
func allEqualUsingSet<T : Hashable>(array : [T]) -> Bool {
let uniqueElements = Set(array)
return count(uniqueElements) <= 1
}
快速基准测试表明,“包含”方法比“设置”方法快得多
对于 1,000,000 个整数的数组,特别是如果元素是
不都相等。这是有道理的,因为contains() 会尽快返回
作为一个不匹配的元素被发现,而Set(array)总是
遍历整个数组。
“包含”方法也比显式循环同样快或略快。
这是一些简单的基准测试代码。当然结果可能会有所不同 与数组大小、不同元素的数量和元素数据类型有关。
func measureExecutionTime<T>(title: String, @noescape f : (() -> T) ) -> T {
let start = NSDate()
let result = f()
let end = NSDate()
let duration = end.timeIntervalSinceDate(start)
println("\(title) \(duration)")
return result
}
var array = [Int](count: 1_000_000, repeatedValue: 1)
array[500_000] = 2
let b1 = measureExecutionTime("using loop ") {
return allEqualUsingLoop(array)
}
let b2 = measureExecutionTime("using contains") {
allEqualUsingContains(array)
}
let b3 = measureExecutionTime("using set ") {
allEqualUsingSet(array)
}
结果(在 MacBook Pro 上,发布配置):
使用循环 0.000651001930236816 使用包含 0.000567018985748291 使用集合 0.0344770550727844array[1_000] = 2 的结果是
Swift 2/Xcode 7 更新:由于 Swift 中的各种变化 语法,函数现在写成
func allEqual<T : Equatable>(array : [T]) -> Bool {
if let firstElem = array.first {
return !array.dropFirst().contains { $0 != firstElem }
}
return true
}
但您现在也可以将其定义为数组的扩展方法:
extension Array where Element : Equatable {
func allEqual() -> Bool {
if let firstElem = first {
return !dropFirst().contains { $0 != firstElem }
}
return true
}
}
print([1, 1, 1].allEqual()) // true
print([1, 2, 1].allEqual()) // false
【讨论】:
equal和Repeat:array.first.map { equal(array, Repeat(count: array.count, repeatedValue: $0)) } ?? true(速度较慢)
array[500_000] = 2 测量为 0.001789 秒,array[1_000] = 2 测量为 3.99e-06 秒。
Swift 4.2/Xcode 10 的解决方案:
let arr = [1, 1, 1, 1]
let allItemsEqual = arr.dropLast().allSatisfy { $0 == arr.last }
print(allItemsEqual)
如果您当前的 Xcode 版本低于 10.0,您可以在 Xcode9to10Preparation 中找到 ArraySlice 的函数 allSatisfy。你可以用 CocoaPods 安装这个库。
【讨论】:
dropLast()。
dropLast() 很重要。在我的示例中,如果我将删除 dropLast() 传递给 allSatisfy 的块将被调用 4 次,但现在它只调用了 3 次,结果是一样的。
dropLast()。