【发布时间】:2021-01-24 11:39:44
【问题描述】:
我在网上遇到过很多例子,当他们试图符合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 的正确方法吗?我认为我们应该考虑所有类成员变量?
例如,在func hash/func == 中仅使用id,将产生以下不当行为。
我们将遇到 2 个内容不同的对象,但 func == 在比较 2 个内容不同的对象时会返回 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 之前, 字符串散列函数最多使用 16 个均匀分布的字符 整个字符串,从第一个字符开始。对于大 分层名称的集合,例如 URL,此函数 完全显示了前面描述的病理行为。
【问题讨论】:
-
视情况而定。从 diffable 数据源的角度来看,如果它是可变的,那么内容很重要,所以
combinehash(into:)方法中的所有相关结构成员。