-hash
的文档说,当一个可变对象存储在一个集合中时,它不能改变,同样-isEqual:
的文档说-hash
值必须相同才能相等对象。
鉴于此,是否有人对实施-hash
的最佳方式有任何建议,使其满足这两个条件但实际上是智能计算的(即不仅仅返回0
)?有人知道框架提供的类的可变版本是如何做到的吗?
最简单的事情当然是忘记第一个条件(关于它不会改变)并确保我从不会在一个集合中偶然改变一个对象,但我想知道是否有任何更灵活的解决方案
编辑:我在想这里是否有可能维持2个合约(当对象在集合中时,相等的对象具有相等的哈希值,并且哈希值不会改变) m改变对象的内部状态。我倾向于说“不”,除非我做一些愚蠢的事情,就像总是返回哈希值,但这就是我问这个问题的原因。
答案 0 :(得分:3)
有趣的问题,但我认为你想要的东西在逻辑上是不可能的。假设您从2个对象A和B开始。它们都是不同的,并且它们以不同的哈希码开头。您将两者都添加到某个哈希表。现在,您想要改变A,但是您无法更改哈希码,因为它已经在表中。但是,可以以.equals()B。
的方式更改A.在这种情况下,您有两个选择,两个都不起作用:
在我看来,如果不使用常量作为哈希码,就没有办法做到这一点。
答案 1 :(得分:2)
我对文档的阅读是hash
的可变对象的值可以(并且可能应该)在发生变异时发生变化,但不应该更改当对象没有发生变异时。因此,要引用的文档部分是“不要改变存储在集合中的对象,因为这会导致它们的hash
值发生变化。”
直接引用NSObject documentation for hash
:
如果将可变对象添加到 使用哈希值的集合 确定对象在中的位置 集合,返回的值 对象的哈希方法一定不能 在对象所在的时候改变 采集。 因此,无论是哈希 方法一定不能依赖任何一个 对象的内部状态信息或 你必须确保对象的 内部国家信息没有 在对象所在的时候改变 集合
(强调我的。)
答案 2 :(得分:1)
这里的问题不是如何满足这两个要求,而是你应该满足哪一个要求。在Apple的文档中,明确指出:
可变字典可以放在哈希表中,但是当它在那里时你不能改变它。
这就是说,你满足哈希的平等要求似乎更重要。对象的哈希值应始终是检查对象是否等于另一个对象的方法。如果情况并非如此,那么它不是真正的哈希函数。
为了完成我的回答,我将给出一个好的哈希实现的例子。假设您正在为您创建的集合编写-hash
的实现。此集合将一组NSObject存储为指针。由于所有NSObject都实现了哈希函数,因此您可以在计算集合的哈希时使用它们的哈希值:
- (NSUInteger)hash {
NSUInteger theHash = 0;
for (NSObject * aPtr in self) { // fast enumeration
theHash ^= [aPtr hash];
}
return theHash;
}
这样,包含相同指针(以相同顺序)的两个集合对象将具有相同的散列。
答案 3 :(得分:0)
既然你已经覆盖-isEqual:要进行基于值的比较,你确定你真的需要打扰-hash吗?
我无法猜测你究竟需要什么,但是如果你想在不偏离-isEqual的预期实现的情况下进行基于价值的比较:当哈希相同时只返回YES,更好的方法可能要模仿NSString的-isEqualToString :,所以创建自己的-isEqualToFoo:方法而不是使用或覆盖-isEqual:。
答案 4 :(得分:-1)
这个问题的答案以及避免许多可可虫的关键是:
仔细阅读文档。将每个单词和标点符号放到金色标尺上,然后称重,因为它是世界上最后一颗小麦。
让我们再次阅读文档:
如果将可变对象添加到使用哈希值确定集合中 对象的位置 的集合中,则[...]
(重点是我的)。
文档作者的永恒智慧意味着,当您实现诸如字典之类的集合时,请勿使用散列进行定位,因为这可能会发生变化。换句话说,它与在可变的Cocoa对象上实现-hash无关(假设问题提出以来的过去10年中,文档没有发生任何变化,我们所有人都认为这样做)。
这就是字典总是复制其键的原因-这样他们可以保证 哈希值不会改变。
然后您将问一个问题:但是,先生,NSMapTable和类似的东西如何处理?
答案根据文档:
“其键或值 可以复制到输入中,或者 可以 使用指针标识进行相等性和散列。”
(再次强调我的意思。)
由于上次我们很容易被文档所迷惑,因此让我们进行一些实验,以了解这些东西的实际工作方式:
NSMutableString *string = [NSMutableString stringWithString:@"so lets mutate this"];
NSString *originalString = string.copy;
NSMapTable *mutableStrings = [NSMapTable strongToStrongObjectsMapTable];
[mutableStrings setObject:originalString forKey:string];
[string appendString:@" into a larger string"];
if ([mutableStrings objectForKey:string] == nil)
NSLog(@"not found!");
if ([mutableStrings objectForKey:originalString] == nil)
NSLog(@"Not even the original string is found?");
for (NSString *inCollection in mutableStrings)
{
NSLog(@"key '%@' : is '%@' (null)", inCollection, [mutableStrings objectForKey:inCollection]);
}
for (NSString *value in NSAllMapTableValues(mutableStrings))
{
NSLog(@"value exists: %@", value);
}
惊奇!
因此,在这里不使用指针相等,而是将重点放在单词“可能”(在这种情况下表示“可能不会”)上,并在向集合中添加内容时简单地复制哈希值。
(所有这些实际上都很好,因为实现NSHashMap或-hash会非常困难)。
答案 5 :(得分:-2)
在Java中,大多数可变类只是不覆盖Object.hashCode(),因此默认实现返回一个基于对象地址的值,并且不会更改。它可能与Objective C相同。