A lot of online example I have came across, when they try to conform to Hashable
, they only take id
as consideration. For instance 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
}
}
I was wondering, is that ever a correct way to conform Hashable
? I thought we should take all class member variables, into consideration?
For instance, by using only id
in func hash
/ func ==
, will yield the following misbehaviour.
We are going encounter 2 objects with different content, but func ==
will return true when comparing 2 objects with different content.
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)")
I was wondering, is it right to conform Hashable
by only taking id
into consideration?
p/s
I try to look from other languages like Java, on what is the general advice regarding hash code generation . This is what is being written in their popular Effective Java book.
Do not be tempted to exclude significant fields from the hash code computation to improve performance. While the resulting hash function may run faster, its poor quality may degrade hash tables’ performance to the point where they become unusable. In particular, the hash function may be confronted with a large collection of instances that differ mainly in regions you’ve chosen to ignore. If this happens, the hash function will map all these instances to a few hash codes, and programs that should run in linear time will instead run in quadratic time. This is not just a theoretical problem. Prior to Java 2, the String hash function used at most sixteen characters evenly spaced throughout the string, starting with the first character. For large collections of hierarchical names, such as URLs, this function displayed exactly the pathological behavior described earlier.
Whether it is correct or not to only use a subset of properties for a
Hashable
conformance completely depends on your requirements.If for a certain object, equality is really only defined by a single variable (or a subset of variables), than it is correct to use that subset of variables for the
Hashable
(andEquatable
conformances).However, if all properties of a type are required to decide whether two instances are equal or not, then you should use all properties.