【发布时间】:2010-11-09 21:18:30
【问题描述】:
注意:以下 SO 问题是相关的,但它们和链接的资源似乎都不能完全回答我的问题,特别是在为对象集合实现相等性测试方面.
- Best practices for overriding -isEqual: and -hash
- Techniques for implementing -hash on mutable Cocoa objects
背景
NSObject 提供-hash(返回实例地址,如(NSUInteger)self)和-isEqual:(返回NO,除非接收者的地址和参数相同)。这些方法被设计为在必要时被覆盖,但文档清楚地表明您应该提供两者或都不提供。此外,如果-isEqual: 为两个对象返回YES,那么这些对象的-hash 的结果必须 相同。否则,当应该相同的对象(例如 -compare: 返回 NSOrderedSame 的两个字符串实例)被添加到 Cocoa 集合或直接比较时,就会出现问题。
上下文
我开发了 CHDataStructures.framework,这是一个 Objective-C 数据结构的开源库。我已经实现了许多集合,目前正在改进和增强它们的功能。我要添加的功能之一是能够比较集合是否相等。
这些比较不应仅比较内存地址,而应考虑两个集合中存在的对象(包括排序,如果适用)。这种方式在Cocoa中有相当的先例,一般采用单独的方式,包括以下几种:
-[NSArray isEqualToArray:]-[NSDate isEqualToDate:]-[NSDictionary isEqualToDictionary:]-[NSNumber isEqualToNumber:]-[NSSet isEqualToSet:]-[NSString isEqualToString:]-[NSValue isEqualToValue:]
我想让我的自定义集合对相等性测试具有鲁棒性,因此它们可以安全地(并且可预测地)添加到其他集合中,并允许其他集合(如 NSSet)确定两个集合是否相等/等价/重复。
问题
-isEqualTo...: 方法本身工作得很好,但定义这些方法的类通常也会覆盖 -isEqual: 以调用 [self isEqualTo...:] 如果参数与接收者属于同一类(或可能是子类),或者 @ 987654353@ 否则。这意味着该类还必须定义 -hash 以便它为具有相同内容的不同实例返回相同的值。
此外,Apple 的 -hash 文档规定如下:(强调我的)
“如果将可变对象添加到使用哈希值确定对象在集合中的位置的集合中,则当对象在集合中时,对象的哈希方法返回的值不得更改。因此,要么散列方法不能依赖任何对象的内部状态信息或者你必须确保对象的内部状态信息在对象处于集合。因此,例如,可以将可变字典放入哈希表中,但当它在其中时,您不能更改它。(请注意,可能很难知道给定对象是否在集合中。) "
编辑: 我完全理解为什么这是必要的,并且完全同意这个推理——我在这里提到它是为了提供额外的背景,并避开了为什么会这样的话题简洁。
我的所有集合都是可变的,并且哈希必须考虑至少 一些 的内容,所以这里唯一的选择是认为改变存储在另一个集合中的集合是一个编程错误收藏。 (我的收藏都采用NSCopying,所以像NSDictionary这样的收藏可以成功复制做key等)
实现-isEqual: 和-hash 对我来说是有意义的,因为(例如)我的一个类的间接用户可能不知道要调用的具体-isEqualTo...: 方法,甚至不关心两个对象是否是同一类的实例。他们应该能够对id 类型的任何变量调用-isEqual: 或-hash 并获得预期的结果。
与-isEqual:(可以访问正在比较的两个实例)不同,-hash 必须“盲目”返回结果,只能访问特定实例中的数据。 由于它无法知道哈希的用途,因此结果必须与 所有 应被视为相等/相同的可能实例一致,并且必须始终与-isEqual:一致/罢工>。 (编辑:这已经被下面的答案揭穿了,它确实让生活更轻松。)此外,编写好的哈希函数并非易事——保证唯一性是一个挑战,尤其是当你只有一个NSUInteger(32/64 位)来表示它。
问题
- 在为集合实施
平等比较-hash时是否有最佳做法? - 在 Objective-C 和 Cocoa 风格的集合中是否有任何特殊性需要规划?
- 是否有任何良好的单元测试方法
-hash具有合理的置信度? - 关于实现
-hash以同意-isEqual:对于包含任意类型元素的集合的任何建议?我应该知道哪些陷阱? (编辑: 没有我最初想的那么成问题——正如 @kperryua 指出的那样,“等于-hash值确实不 暗示-isEqual:"。)
编辑: 我应该澄清一下,我对如何为集合实现 -isEqual: 或 -isEqualTo...: 并不感到困惑,这很简单。我认为我的困惑主要源于(错误地)认为如果 -isEqual: 返回 NO,则 -hash 必须返回不同的值。过去做过密码学,我认为不同值的哈希值必须不同。然而,下面的答案让我意识到一个“好的”哈希函数实际上是关于最小化桶冲突和链接使用-hash的集合。虽然唯一的哈希值更可取,但它们并不是严格要求。
【问题讨论】:
标签: objective-c cocoa data-structures equality chdatastructures