重写 isEqual: 和 hash 的最佳做法

Best practices for overriding isEqual: and hash

提问人:Dave Dribin 提问时间:11/1/2008 最后编辑:Quinn TaylorDave Dribin 更新时间:1/21/2022 访问量:95790

问:

如何在 Objective-C 中正确覆盖?“陷阱”似乎是,如果两个对象相等(由方法确定),它们必须具有相同的哈希值。isEqual:isEqual:

Cocoa 基础指南Introspection 部分确实有一个关于如何覆盖的示例,复制如下,用于一个名为 :isEqual:MyWidget

- (BOOL)isEqual:(id)other {
    if (other == self)
        return YES;
    if (!other || ![other isKindOfClass:[self class]])
        return NO;
    return [self isEqualToWidget:other];
}

- (BOOL)isEqualToWidget:(MyWidget *)aWidget {
    if (self == aWidget)
        return YES;
    if (![(id)[self name] isEqual:[aWidget name]])
        return NO;
    if (![[self data] isEqualToData:[aWidget data]])
        return NO;
    return YES;
}

它检查指针是否相等,然后检查类是否相等,最后使用 比较对象,它只检查 和 属性。该示例显示的是如何覆盖 。isEqualToWidget:namedatahash

让我们假设还有其他不影响相等的属性,比如 。难道不应该重写该方法,以便仅影响哈希值吗?如果是这样,你会怎么做?只需将 和 ?例如:agehashnamedatanamedata

- (NSUInteger)hash {
    NSUInteger hash = 0;
    hash += [[self name] hash];
    hash += [[self data] hash];
    return hash;
}

这够了吗?有没有更好的技术?如果你有原语怎么办,比如?将它们转换为以获取它们的哈希值?或者像这样的结构?intNSNumberNSRect

(脑屁:最初将它们与 .意思是添加。|=

Objective-C 相等性

评论

2赞 Robert 11/21/2013
if (![other isKindOfClass:[self class]])- 从技术上讲,这意味着相等不会是可交换的。即 A = B 并不意味着 B = A(例如,如果一个是另一个的子类)
0赞 jedwidz 1/2/2019
文档链接已失效,现已存档到 Introspection

答:

12赞 Jens Ayton 11/1/2008 #1

简单但效率低下的方法是为每个实例返回相同的值。否则,是的,您必须仅基于影响相等性的对象实现哈希。如果您在中使用松散的比较(例如,不区分大小写的字符串比较),这将很棘手。对于 ints,您通常可以使用 int 本身,除非您将与 NSNumbers 进行比较。-hash-isEqual:

但是不要使用 |=,它会饱和。请改用 ^=。

随机有趣的事实: , 但是 .(rdar://4538282,自 2006 年 5 月 5 日开始营业)[[NSNumber numberWithInt:0] isEqual:[NSNumber numberWithBool:NO]][[NSNumber numberWithInt:0] hash] != [[NSNumber numberWithBool:NO] hash]

评论

1赞 Dave Dribin 11/1/2008
你在 |= 上完全正确。其实不是那个意思。:) += 和 ^= 相当等效。如何处理非整数原语,如 double 和 float?
0赞 Quinn Taylor 7/11/2009
随机有趣的事实:在雪豹上测试它...... ;-)
0赞 Quinn Taylor 7/11/2009
他关于使用 XOR 而不是 OR 将字段组合成哈希是正确的。但是,不要使用为每个对象返回相同的 -hash 值的建议——尽管这很容易,但它可能会严重降低使用对象哈希的任何内容的性能。对于不相等的对象,哈希值不必不同,但如果你能做到这一点,那就没有比这更好的了。
0赞 JJD 11/19/2010
打开的雷达错误报告已关闭。openradar.me/4538282那是什么意思?
0赞 Jens Ayton 11/19/2010
JJD,正如 Quinn 暗示的那样,该错误已在 Mac OS X 10.6 中修复。(请注意,该评论已有两年历史。
5赞 mipadi 11/1/2008 #2

我发现此页面是覆盖等于和哈希类型方法的有用指南。它包括一个不错的算法来计算哈希码。该页面面向 Java,但很容易将其改编为 Objective-C/Cocoa。

评论

1赞 cobbal 5/20/2010
通过 archive.org 缓存链接:web.archive.org/web/20071013053633/http://www.geocities.com/...
5赞 schwa 11/1/2008 #3

这并不能直接回答您的问题(根本没有),但我以前使用过 MurmurHash 来生成哈希: murmurhash

我想我应该解释一下原因:murmurhash 是血腥的......

评论

2赞 Quinn Taylor 7/11/2009
专注于使用随机数的 void* 键的唯一哈希值的 C++ 库(也与 Objective-C 对象无关)在这里确实不是一个有用的建议。-hash 方法每次都应返回一致的值,否则将完全无用。如果将对象添加到调用 -hash 的集合中,并且每次都返回一个新值,则永远不会检测到重复项,并且您也永远无法从集合中检索该对象。在这种情况下,术语“哈希”与安全/密码学中的含义不同。
3赞 schwa 1/23/2010
Murmurhash 不是加密哈希函数。在发布不正确的信息之前,请检查您的事实。Murmurhash 对于散列自定义 objective-c 类(特别是如果你涉及大量 NSData)很有用,因为它非常快。但是,我确实同意,也许建议这不是给“刚刚拿起 objective-c”的人的最佳建议,但请注意我对这个问题的原始回复中的前缀。
115赞 tcurdt 11/1/2008 #4

入手

 NSUInteger prime = 31;
 NSUInteger result = 1;

然后,对于你所做的每一个基元

 result = prime * result + var

对于对象,您使用 0 表示 nil,否则使用它们的哈希码。

 result = prime * result + [var hash];

对于布尔值,使用两个不同的值

 result = prime * result + ((var)?1231:1237);

解释和归属

这不是 tcurdt 的作品,评论要求更多解释,所以我相信对归属的编辑是公平的。

该算法在《Effective Java》一书中得到了推广,相关章节目前可以在这里在线找到。那本书普及了该算法,该算法现在是许多 Java 应用程序(包括 Eclipse)的默认算法。然而,它源自一个更古老的实现,该实现被归因于 Dan Bernstein 或 Chris Torek。这种较旧的算法最初在Usenet上流传,并且很难确定归因。例如,这个 Apache 代码中有一些有趣的注释(搜索它们的名称)引用了原始源代码。

最重要的是,这是一个非常古老、简单的哈希算法。它不是性能最高的,甚至没有在数学上被证明是一个“好”的算法。但是它很简单,而且很多人用了很长时间,效果很好,所以有很多历史支持。

评论

10赞 David Leonard 3/9/2009
1231:1237从何而来?我也在 Java 的 Boolean.hashCode() 中看到了它。它神奇吗?
17赞 tcurdt 8/5/2011
哈希算法的本质是会有冲突。所以我不明白你的意思,保罗。
85赞 Ricardo Sanchez-Saez 10/13/2011
在我看来,这个答案并没有回答实际问题(覆盖 NSObject 哈希的最佳实践)。它只提供一种特定的哈希算法。最重要的是,解释的稀疏性使得如果没有对此事的深入了解就很难理解,并可能导致人们在不知道自己在做什么的情况下使用它。我不明白为什么这个问题有这么多赞成票。
6赞 Paul Solt 1/6/2012
第一个问题 - (int) 很小,容易溢出,使用 NSUInteger。第二个问题 - 如果你一直将结果乘以每个变量哈希,你的结果就会溢出。例如。[NSString 哈希] 创建较大的值。如果你有 5+ 个变量,这个算法很容易溢出。这将导致所有内容都映射到相同的哈希值,这很糟糕。请看我的回复:stackoverflow.com/a/4393493/276626
11赞 DougW 6/15/2012
@PaulSolt - 溢出不是生成哈希的问题,冲突是。但是溢出并不一定会增加冲突的可能性,你关于溢出导致所有内容映射到同一哈希值的说法是完全不正确的。
81赞 Brian B. 11/1/2008 #5

我自己只是拿起 Objective-C,所以我不能专门代表该语言,但在我使用的其他语言中,如果两个实例“相等”,它们必须返回相同的哈希值 - 否则,当尝试将它们用作哈希表(或任何字典类型集合)中的键时,您将遇到各种问题。

另一方面,如果 2 个实例不相等,它们可能具有也可能不具有相同的哈希值 - 最好不要。这就是对哈希表进行 O(1) 搜索和 O(N) 搜索之间的区别 - 如果所有哈希值都发生冲突,您可能会发现搜索表并不比搜索列表好。

就最佳实践而言,哈希值应返回其输入值的随机分布。这意味着,例如,如果您有双精度值,但大多数值倾向于聚集在 0 到 100 之间,则需要确保这些值返回的哈希值均匀分布在可能的哈希值的整个范围内。这将显着提高您的表现。

市面上有许多哈希算法,包括这里列出的几种。我尽量避免创建新的哈希算法,因为它可能会对性能产生很大影响,因此使用现有的哈希方法并执行某种按位组合(如示例中所示)是避免这种情况的好方法。

评论

4赞 Quinn Taylor 7/11/2009
+1 优秀的答案,值得更多的点赞,特别是因为他实际上谈到了“最佳实践”以及为什么一个好的(唯一)哈希很重要的理论。
2赞 user10345 11/3/2008 #6

请注意,如果要创建可在创建后进行更改的对象,则在将对象插入集合时,哈希值不得更改。实际上,这意味着哈希值必须从初始对象创建点开始固定。有关详细信息,请参阅 Apple 关于 NSObject 协议的 -hash 方法的文档

如果将可变对象添加到使用哈希值来确定对象在集合中的位置的集合中,则当对象位于集合中时,该对象的哈希方法返回的值不得更改。因此,哈希方法不得依赖于对象的任何内部状态信息,或者必须确保对象的内部状态信息在集合中时不会更改。因此,例如,可以将可变字典放在哈希表中,但当它位于哈希表中时,您不能更改它。(请注意,可能很难知道给定对象是否在集合中。

这对我来说听起来像是彻头彻尾的怪话,因为它可能会有效地使哈希查找的效率大大降低,但我认为最好谨慎行事并遵循文档所说的内容。

评论

1赞 Quinn Taylor 7/11/2009
你看错了哈希文档——这本质上是一种“非此即彼”的情况。如果对象发生更改,则哈希值通常也会发生更改。这实际上是对程序员的警告,如果哈希值由于对象突变而更改,那么当对象驻留在使用哈希的集合中时,更改该对象将导致意外行为。如果在这种情况下对象必须是“安全可变的”,则别无选择,只能使哈希与可变状态无关。这种特殊情况对我来说听起来确实很奇怪,但肯定很少出现这种情况。
4赞 ceperry 2/24/2009 #7

我也是目标 C 的新手,但我在这里找到了一篇关于目标 C 中身份与平等的优秀文章。从我的阅读来看,您似乎可以保留默认的哈希函数(它应该提供唯一的标识)并实现 isEqual 方法,以便它比较数据值。

评论

0赞 John Gallagher 12/15/2009
我是 Cocoa/Objective C 新手,这个答案和链接确实帮助我完成了上面所有更高级的东西,达到了底线 - 我不需要担心哈希值 - 只需实现 isEqual: 方法。谢谢!
0赞 JJD 11/19/2010
不要错过@ceperry的链接。卡尔·卡夫(Karl Kraft)的文章真的很好。Equality vs Identity
6赞 Steve Madsen 3/16/2011
@John:我认为你应该重新阅读这篇文章。它非常清楚地表明“相等的实例必须具有相等的哈希值”。如果覆盖 ,则还必须覆盖 。isEqual:hash
3赞 Ces 9/1/2009 #8

Quinn 错了,这里对杂音哈希的引用毫无用处。Quinn说得对,你想了解哈希背后的理论。杂音将许多理论提炼成一种实现。弄清楚如何将该实现应用于此特定应用程序是值得探索的。

以下是一些关键点:

tcurdt 的示例函数表明 '31' 是一个很好的乘数,因为它是素数。人们需要证明成为素数是一个必要和充分的条件。事实上,31(和 7)可能不是特别好的素数,因为 31 == -1 % 32。设置了大约一半位和清除一半位的奇数乘法器可能会更好。(杂音哈希乘法常量具有该属性。

如果在乘法后通过移位和异或调整结果值,则这种类型的哈希函数可能会更强大。乘法往往会在寄存器的高端产生大量位交互的结果,而在寄存器的底端产生低交互结果。移位和异或增加了寄存器底端的交互作用。

将初始结果设置为大约一半的位为零,大约一半的位为1的值也往往很有用。

注意元素组合的顺序可能会有所帮助。人们可能应该首先处理布尔值和其他值分布不强的元素。

在计算结束时添加几个额外的位加扰阶段可能很有用。

对于此应用程序,杂音哈希是否真的很快是一个悬而未决的问题。杂音哈希预混淆每个输入字的位。可以并行处理多个输入字,这有助于多问题流水线 CPU。

1赞 Thibaud de Souza 11/6/2009 #9

对不起,如果我冒着在这里听起来完全是棺材的风险,但是...... ...没有人费心提到,要遵循“最佳实践”,您绝对不应该指定一个不考虑目标对象拥有的所有数据的 equals 方法,例如,在实现 equals 时应该考虑聚合到对象的任何数据,而不是它的关联。 如果你不想在比较中考虑“年龄”,那么你应该写一个比较器,用它来执行你的比较,而不是isEqual:。

如果定义一个任意执行相等比较的 isEqual: 方法,则一旦忘记了相等解释中的“扭曲”,就会产生该方法被其他开发人员甚至您自己滥用的风险。

因此,虽然这是一个关于哈希的很好的问答,但你通常不需要重新定义哈希方法,你可能应该定义一个临时比较器。

29赞 LavaSlider 11/27/2010 #10

我发现这个线程非常有用,它提供了我实现我和方法所需的一切。在示例代码中测试对象实例变量时,使用:isEqual:hashisEqual:

if (![(id)[self name] isEqual:[aWidget name]])
    return NO;

我知道这些对象在我的单元测试中是相同的时,这反复失败(返回 NO)而没有错误。原因是,其中一个实例变量为 nil,因此上面的语句是:NSString

if (![nil isEqual: nil])
    return NO;

由于 NIL 会响应任何方法,这是完全合法的,但

[nil isEqual: nil]

返回 nil,即 NO,因此当对象和被测试对象都具有 nil 对象时,它们将被视为不相等(返回 NO)。isEqual:

这个简单的解决方法是将 if 语句更改为:

if ([self name] != [aWidget name] && ![(id)[self name] isEqual:[aWidget name]])
    return NO;

这样,如果它们的地址相同,则无论它们是否都指向同一对象,它都会跳过方法调用,但如果它们不是 nil 或它们指向不同的对象,则适当地调用比较器。

我希望这能为某人节省几分钟的挠头时间。

22赞 Paul Solt 12/9/2010 #11

哈希函数应创建一个半唯一值,该值不太可能与其他对象的哈希值发生冲突或匹配。

这是完整的哈希函数,可以适应您的类实例变量。它使用 NSUInteger 而不是 int 来实现 64/32 位应用程序的兼容性。

如果不同对象的结果变为 0,则存在哈希冲突的风险。在使用某些依赖于哈希函数的集合类时,冲突哈希可能会导致意外的程序行为。请务必在使用前测试您的哈希函数。

-(NSUInteger)hash {
    NSUInteger result = 1;
    NSUInteger prime = 31;
    NSUInteger yesPrime = 1231;
    NSUInteger noPrime = 1237;
    
    // Add any object that already has a hash function (NSString)
    result = prime * result + [self.myObject hash];
    
    // Add primitive variables (int)
    result = prime * result + self.primitiveVariable; 

    // Boolean values (BOOL)
    result = prime * result + (self.isSelected ? yesPrime : noPrime);
    
    return result;
}

评论

3赞 Ashley 6/12/2013
这里有一个问题:我更喜欢避免点语法,所以我将您的 BOOL 语句转换为 (eg) 。然后我发现它被设置为(例如),我假设是由于操作员优先。我通过添加括号解决了这个问题:result = prime * result + [self isSelected] ? yesPrime : noPrime;result1231?result = prime * result + ([self isSelected] ? yesPrime : noPrime);
7赞 Jonathan Ellis 1/18/2012 #12

等等,当然,一个更简单的方法是首先覆盖并提供对象状态的字符串表示形式(您必须在此字符串中表示对象的整个状态)。- (NSString )description

然后,只需提供以下实现:hash

- (NSUInteger)hash {
    return [[self description] hash];
}

这是基于以下原则:如果两个字符串对象相等(由 isEqualToString: 方法确定),则它们必须具有相同的哈希值。

来源: NSString 类参考

评论

1赞 Paul Solt 6/25/2012
这假定 description 方法是唯一的。使用描述的哈希会产生依赖关系,这可能并不明显,并且发生冲突的风险更高。
1赞 user4951 9/23/2012
+1 赞成。这是一个很棒的主意。如果您担心描述会导致冲突,则可以覆盖它。
0赞 Jonathan Ellis 9/25/2012
谢谢吉姆,我不否认这有点黑客攻击,但它在我能想到的任何情况下都会起作用——正如我所说,前提是你覆盖,我不明白为什么这不如任何更高票数的解决方案。可能不是数学上最优雅的解决方案,但应该可以解决问题。正如 Brian B. 所说(在这一点上,大多数赞成的答案):“我尽量避免创建新的哈希算法”——同意!-- 我只是 !descriptionhashNSString
0赞 karwag 5/7/2013
被点赞是因为这是一个不错的主意。不过,我不打算使用它,因为我担心额外的 NSString 分配。
1赞 Diogo T 4/7/2016
这不是一个通用的解决方案,因为对于大多数类来说,都包含指针地址。因此,这使得同一类的两个不同实例以不同的哈希值相等,这违反了两个相等的对象具有相同哈希值的基本假设!description
9赞 user4951 9/24/2012 #13

请记住,您只需要提供在 true 时相等的哈希值。当为 false 时,哈希值不必不相等,尽管它可能是不相等的。因此:isEqualisEqual

保持哈希简单。选择一个(或几个成员)最独特的成员变量。

例如,对于 CLPlacemark,仅名称就足够了。是的,有 2 或 3 个名称完全相同的不同 CLPlacemark,但这些都很少见。使用该哈希值。

@interface CLPlacemark (equal)
- (BOOL)isEqual:(CLPlacemark*)other;
@end

@implementation CLPlacemark (equal)

...

-(NSUInteger) hash
{
    return self.name.hash;
}


@end

请注意,我懒得指定城市、国家等。这个名字就足够了。也许是名称和 CLLocation。

哈希值应均匀分布。因此,您可以使用插入符号 ^(异或符号)组合多个成员变量

所以它类似于

hash = self.member1.hash ^ self.member2.hash ^ self.member3.hash

这样,哈希值将均匀分布。

Hash must be O(1), and not O(n)

那么在数组中做什么呢?

再次,简单。您不必对数组的所有成员进行哈希处理。足以对第一个元素、最后一个元素、计数,也许还有一些中间元素进行哈希处理,仅此而已。

评论

0赞 fishinear 3/28/2020
异运算哈希值不会给出均匀分布。
8赞 jedwidz 5/19/2013 #14

在 Java 世界中,equals 和 hash 合约被很好地指定和深入研究(参见 @mipardi 的回答),但所有相同的考虑都应该适用于 Objective-C。

Eclipse 在 Java 中生成这些方法方面做得非常可靠,所以这里有一个手动移植到 Objective-C 的 Eclipse 示例:

- (BOOL)isEqual:(id)object {
    if (self == object)
        return true;
    if ([self class] != [object class])
        return false;
    MyWidget *other = (MyWidget *)object;
    if (_name == nil) {
        if (other->_name != nil)
            return false;
    }
    else if (![_name isEqual:other->_name])
        return false;
    if (_data == nil) {
        if (other->_data != nil)
            return false;
    }
    else if (![_data isEqual:other->_data])
        return false;
    return true;
}

- (NSUInteger)hash {
    const NSUInteger prime = 31;
    NSUInteger result = 1;
    result = prime * result + [_name hash];
    result = prime * result + [_data hash];
    return result;
}

对于添加属性的子类:YourWidgetserialNo

- (BOOL)isEqual:(id)object {
    if (self == object)
        return true;
    if (![super isEqual:object])
        return false;
    if ([self class] != [object class])
        return false;
    YourWidget *other = (YourWidget *)object;
    if (_serialNo == nil) {
        if (other->_serialNo != nil)
            return false;
    }
    else if (![_serialNo isEqual:other->_serialNo])
        return false;
    return true;
}

- (NSUInteger)hash {
    const NSUInteger prime = 31;
    NSUInteger result = [super hash];
    result = prime * result + [_serialNo hash];
    return result;
}

此实现避免了 Apple 示例中的一些子类陷阱:isEqual:

  • Apple 的类测试对于 的两个不同的子类是不对称的。相等必须是对称的:当且仅当 b=a,a=b。这可以通过将测试更改为 来轻松解决,然后所有子类都将相互比较。other isKindOfClass:[self class]MyWidgetother isKindOfClass:[MyWidget class]MyWidget
  • 使用子类测试可防止子类被优化的相等性测试覆盖。这是因为相等必须是可传递的:如果 a=b 和 a=c,则 b=c。如果一个实例的比较等于两个实例,则这些实例必须彼此相等,即使它们不同。isKindOfClass:isEqual:MyWidgetYourWidgetYourWidgetserialNo

第二个问题可以通过仅考虑对象在属于完全相同的类时相等来解决,因此这里进行了测试。对于典型的应用程序类,这似乎是最好的方法。[self class] != [object class]

但是,在某些情况下,该测试是可取的。这在框架类中比在应用程序中更典型。例如,无论 / 的区别如何,也无论类集群中涉及哪些私有类,任何都应该与具有相同基础字符序列的任何其他类相等进行比较。isKindOfClass:NSStringNSStringNSStringNSMutableStringNSString

在这种情况下,应该有明确定义、有据可查的行为,并且应该明确子类不能覆盖它。在 Java 中,可以通过将 equals 和 hashcode 方法标记为 来强制执行“无覆盖”限制,但 Objective-C 没有等效项。isEqual:final

评论

0赞 Archagon 11/9/2021
这似乎比以下原因更正确:stackoverflow.com/questions/3126476/......isMemberOfClass[self class] != [object class]NSProxy
3赞 johnboiles 10/30/2013 #15

将 @tcurdt 的答案与 @oscar-gomez 获取属性名称的答案结合起来,我们可以为 isEqual 和 hash 创建一个简单的插入式解决方案:

NSArray *PropertyNamesFromObject(id object)
{
    unsigned int propertyCount = 0;
    objc_property_t * properties = class_copyPropertyList([object class], &propertyCount);
    NSMutableArray *propertyNames = [NSMutableArray arrayWithCapacity:propertyCount];

    for (unsigned int i = 0; i < propertyCount; ++i) {
        objc_property_t property = properties[i];
        const char * name = property_getName(property);
        NSString *propertyName = [NSString stringWithUTF8String:name];
        [propertyNames addObject:propertyName];
    }
    free(properties);
    return propertyNames;
}

BOOL IsEqualObjects(id object1, id object2)
{
    if (object1 == object2)
        return YES;
    if (!object1 || ![object2 isKindOfClass:[object1 class]])
        return NO;

    NSArray *propertyNames = PropertyNamesFromObject(object1);
    for (NSString *propertyName in propertyNames) {
        if (([object1 valueForKey:propertyName] != [object2 valueForKey:propertyName])
            && (![[object1 valueForKey:propertyName] isEqual:[object2 valueForKey:propertyName]])) return NO;
    }

    return YES;
}

NSUInteger MagicHash(id object)
{
    NSUInteger prime = 31;
    NSUInteger result = 1;

    NSArray *propertyNames = PropertyNamesFromObject(object);

    for (NSString *propertyName in propertyNames) {
        id value = [object valueForKey:propertyName];
        result = prime * result + [value hash];
    }

    return result;
}

现在,在自定义类中,您可以轻松地实现和:isEqual:hash

- (NSUInteger)hash
{
    return MagicHash(self);
}

- (BOOL)isEqual:(id)other
{
    return IsEqualObjects(self, other);
}
33赞 Yariv Nissim 11/16/2013 #16

对关键属性的哈希值进行简单的异或就足够了 99% 的时间。

例如:

- (NSUInteger)hash
{
    return [self.name hash] ^ [self.data hash];
}

Mattt Thompson 在 http://nshipster.com/equality/ 上找到的解决方案(在他的帖子中也提到了这个问题:~)

评论

2赞 Nat 5/9/2017
这个答案的问题在于它根本没有考虑原始值。基元值对于哈希处理也可能很重要。
0赞 Yariv Nissim 5/11/2017
@Vive 这些问题中的大多数都可以在 Swift 中解决,但这些类型通常代表它们自己的哈希值,因为它们是原始的。
1赞 Nat 5/11/2017
虽然你适合 Swift,但仍然有很多项目是用 objc 编写的。因为你的答案是专门针对 objc 的,所以至少值得一提。
0赞 fishinear 3/28/2020
异运算哈希值在一起是不好的建议,它会导致许多哈希冲突。取而代之的是,乘以质数,然后相加,正如其他答案所述。
0赞 Cœur 1/5/2022
如果你的哈希码在你写的时候“99%的时间就足够了”,那么你也是在说它在1%的时间内导致冲突,这将是灾难性的。换句话说,这句话对你没有好处。