覆盖isEqual:和哈希的最佳实践 [英] Best practices for overriding isEqual: and hash

查看:77
本文介绍了覆盖isEqual:和哈希的最佳实践的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

How do you properly override isEqual: in Objective-C? The "catch" seems to be that if two objects are equal (as determined by the isEqual: method), they must have the same hash value.

可可基础指南的noreferrer>内省部分对于名为MyWidget的类,如何重写isEqual:的示例,如下所示复制:

The Introspection section of the Cocoa Fundamentals Guide does have an example on how to override isEqual:, copied as follows, for a class named 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:比较对象,该对象仅检查namedata属性.示例不显示的内容是如何覆盖hash.

It checks pointer equality, then class equality, and finally compares the objects using isEqualToWidget:, which only checks the name and data properties. What the example doesn't show is how to override hash.

让我们假设还有其他不影响相等性的属性,例如age.不应覆盖hash方法,使仅namedata影响散列吗?如果是这样,您将如何做?只需添加namedata的哈希值?例如:

Let's assume there are other properties that do not affect equality, say age. Shouldn't the hash method be overridden such that only name and data affect the hash? And if so, how would you do that? Just add the hashes of name and data? For example:

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

足够了吗?有更好的技术吗?如果您有诸如int之类的基元怎么办?将它们转换为NSNumber以获取其哈希值?还是NSRect这样的结构?

Is that sufficient? Is there a better technique? What if you have primitives, like int? Convert them to NSNumber to get their hash? Or structs like NSRect?

(大脑放屁:最初将它们与|=一起写成按位或".意味着添加.)

(Brain fart: Originally wrote "bitwise OR" them together with |=. Meant add.)

推荐答案

 NSUInteger prime = 31;
 NSUInteger result = 1;

然后针对您所做的每个原始

Then for every primitive you do

 result = prime * result + var

对于对象,您将0用作nil,否则将其哈希码使用

For objects you use 0 for nil and otherwise their hashcode.

 result = prime * result + [var hash];

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

For booleans you use two different values

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


说明和归因

这不是tcurdt的工作,评论要求提供更多解释,所以我认为对归属的修改是公平的.


Explanation and Attribution

This is not tcurdt's work, and comments were asking for more explanation, so I believe an edit for attribution is fair.

此算法在有效Java"一书中得到了普及,并且与相关目前可以在此处在线找到该章.那本书普及了该算法,该算法现在是许多Java应用程序(包括Eclipse)中的默认算法.但是,它源自甚至更老的实现,该实现被不同地归因于Dan Bernstein或Chris Torek.该较旧的算法最初是在Usenet上浮动的,因此很难确定某些归属.例如,在此Apache代码中有一些有趣的注释(搜索名称).

This algorithm was popularized in the book "Effective Java", and the relevant chapter can currently be found online here. That book popularized the algorithm, which is now a default in a number of Java applications (including Eclipse). It derived, however, from an even older implementation which is variously attributed to Dan Bernstein or Chris Torek. That older algorithm originally floated around on Usenet, and certain attribution is difficult. For example, there is some interesting commentary in this Apache code (search for their names) that references the original source.

最重要的是,这是一种非常古老的简单哈希算法.它不是性能最高的,甚至在数学上都没有证明它是一种好的"算法.但是它很简单,并且很多人已经使用了很长时间,并且效果很好,因此它具有很多的历史支持.

Bottom line is, this is a very old, simple hashing algorithm. It is not the most performant, and it is not even proven mathematically to be a "good" algorithm. But it is simple, and a lot of people have used it for a long time with good results, so it has a lot of historical support.

这篇关于覆盖isEqual:和哈希的最佳实践的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆