我遇到的很多在线示例,当它们尝试符合Hashable
时,仅考虑了id
。例如https://www.raywenderlich.com/8241072-ios-tutorial-collection-view-and-diffable-data-source,https://medium.com/@JoyceMatos/hashable-protocols-in-swift-baf0cabeaebd,...
/// Copyright (c) 2020 Razeware LLC
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to deal
/// in the Software without restriction, including without limitation the rights
/// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
/// copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// Notwithstanding the foregoing, you may not use, copy, modify, merge, publish,
/// distribute, sublicense, create a derivative work, and/or sell copies of the
/// Software in any work that is designed, intended, or marketed for pedagogical or
/// instructional purposes related to programming, coding, application development,
/// or information technology. Permission for such use, copying, modification,
/// merger, publication, distribution, sublicensing, creation of derivative works,
/// or sale is expressly withheld.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
/// THE SOFTWARE.
import UIKit
class Video: Hashable {
var id = UUID()
var title: String
var thumbnail: UIImage?
var lessonCount: Int
var link: URL?
init(title: String, thumbnail: UIImage? = nil, lessonCount: Int, link: URL?) {
self.title = title
self.thumbnail = thumbnail
self.lessonCount = lessonCount
self.link = link
}
// 1
func hash(into hasher: inout Hasher) {
// 2
hasher.combine(id)
}
// 3
static func == (lhs: Video, rhs: Video) -> Bool {
lhs.id == rhs.id
}
}
我在想,是否有一种正确的方法来符合Hashable
协议?我认为我们应该考虑所有类成员变量吧?
例如,仅使用id
在func hash
/func ==
中,将导致以下不良行为。
我们会遇到两个内容不同的对象,但比较这两个对象时func ==
将返回true。
struct Dog: Hashable {
let id = UUID()
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
static func == (lhs: Dog, rhs: Dog) -> Bool {
lhs.id == rhs.id
}
}
var dog0 = Dog(name: "dog", age: 1)
var dog1 = dog0
/*
dog0 is -5743610764084706839, dog, 1
dog1 is -5743610764084706839, dog, 1
compare dog0 with dog1 is true
*/
print("dog0 is \(dog0.hashValue), \(dog0.name), \(dog0.age)")
print("dog1 is \(dog1.hashValue), \(dog1.name), \(dog1.age)")
print("compare dog0 with dog1 is \(dog0 == dog1)")
dog1.name = "another name"
dog1.age = 9
// Same id, but different content!
/*
dog0 is -5743610764084706839, dog, 1
dog1 is -5743610764084706839, another name, 9
compare dog0 with dog1 is true
*/
print("dog0 is \(dog0.hashValue), \(dog0.name), \(dog0.age)")
print("dog1 is \(dog1.hashValue), \(dog1.name), \(dog1.age)")
print("compare dog0 with dog1 is \(dog0 == dog1)")
我想知道,仅考虑 id
是否符合符合 Hashable
的要求?
p/s
我尝试从其他语言(例如Java)中了解有关哈希码生成的一般建议。在他们流行的 Effective Java 书中写下了以下内容。
不要因为性能而诱惑自己从哈希码计算中排除重要字段。虽然生成的哈希函数可能运行得更快,但其质量差劲可能会降低哈希表的性能,甚至使其变得无法使用。特别是,哈希函数可能面临大量只在你选择忽略的区域中有所不同的实例集合。如果发生这种情况,则哈希函数将所有这些实例映射到少量哈希码,并且应该以线性时间运行的程序将代替以二次时间运行。这不仅仅是一个理论问题。在 Java 2 之前,String 哈希函数最多使用字符串中间均匀分布的十六个字符,从第一个字符开始。对于包含大量层次结构名称(例如 URL)的集合,此函数完全显示了早期描述的病态行为。
hash(into:)
方法中将所有相关结构成员组合起来。 - vadian