代码之家  ›  专栏  ›  技术社区  ›  Lily Ballard

在可变cocoa对象上实现-hash的技术

  •  12
  • Lily Ballard  · 技术社区  · 15 年前

    文件 -hash 表示当可变对象存储在集合中时,它不能更改,类似地, -isEqual: -哈希 相同对象的值必须相同。

    考虑到这一点,是否有人对最佳实施方法有任何建议? -哈希 这样它就满足了这两个条件,而且实际上是智能计算的(也就是说,它不只是返回 0 )?有人知道框架提供的类的可变版本是如何做到这一点的吗?

    要做的最简单的事情当然是忘记第一个条件(关于它没有改变),并确保在一个对象在集合中时不会意外地改变它,但是我想知道是否有更灵活的解决方案。

    编辑: 我想知道当我改变对象的内部状态时,是否可以维护这两个契约(相等的对象有相等的哈希,而对象在集合中哈希不变)。我倾向于说“不”,除非我做了一些愚蠢的事情,比如总是返回0作为散列值,但这就是我问这个问题的原因。

    6 回复  |  直到 6 年前
        1
  •  3
  •   Chris Hanson    15 年前

    有意思的问题,但我认为你想要什么在逻辑上是不可能的。假设您从两个对象开始,A和B。它们都是不同的,并且以不同的哈希代码开始。将两者都添加到某个哈希表中。现在,您想要改变一个,但是不能更改哈希代码,因为它已经在表中了。但是,可以更改a,使其等于()b。

    在这种情况下,您有两个选择,都不起作用:

    1. 将a的哈希代码更改为等于b.hashcode,这违反了在哈希表中不更改哈希代码的约束。
    2. 不要更改哈希代码,在这种情况下,a.equals(b)的哈希代码不同。

    在我看来,要做到这一点,不使用常量作为哈希代码是不可能的。

        2
  •  2
  •   Evan DiBiase    15 年前

    我对文档的理解是可变对象的值 hash 可以 (可能应该)在变异时发生变化,但是 不应该 对象未发生变异时更改。因此,要引用的文档部分会说,“不要改变存储在集合中的对象,因为这会导致 搞砸 要更改的值。”

    直接引用 NSObject documentation for hash :

    如果可变对象添加到 使用哈希值的集合 确定对象在 集合,返回的值 对象的哈希方法不能 当对象位于 收集。 因此,要么散列 方法不能依赖于 对象的内部状态信息或 您必须确保对象_ 内部状态信息不 当对象位于 收集 .

    (强调我的)

        3
  •  1
  •   Alex Nichol    13 年前

    这里的问题不是如何满足这两个要求,而是你需要满足哪一个 应该 相遇。在苹果的文档中,明确指出:

    可变字典可以放在哈希表中,但不能在哈希表中更改它。

    也就是说,满足哈希的平等要求似乎更为重要。对象的散列值应该总是一种检查对象是否等于另一个对象的方法。如果不是这样,它就不是真正的哈希函数。

    为了完成我的答案,我将给出一个好的哈希实现示例。假设您正在编写 -hash 在您创建的集合上。此集合将nsObjects数组存储为指针。由于所有nsObject都实现了哈希函数,因此可以在计算集合的哈希时使用它们的哈希:

    - (NSUInteger)hash {
        NSUInteger theHash = 0;
        for (NSObject * aPtr in self) { // fast enumeration
            theHash ^= [aPtr hash];
        }
        return theHash;
    }
    

    这样,包含相同指针(顺序相同)的两个集合对象将具有相同的哈希。

        4
  •  0
  •   Dirk Stoop    15 年前

    既然您已经覆盖了-isequal:要进行基于值的比较,您确定需要考虑-hash吗?

    当然,我不知道您究竟需要这样做是为了什么,但是如果您希望在不偏离-IsEqual:的预期实现的情况下进行基于值的比较,只在哈希相同时返回Yes,更好的方法可能是模仿nsString的-IsEqualToString:,以便创建自己的-IsEqualToFoo:方法,而不是使用或重写IN-等于:

        5
  •  -1
  •   Olof_t    6 年前

    这个问题的答案和避免许多可可虫的关键是:

    仔细阅读文档。把每一个单词和标点都放在一个金色的刻度上,并按它是世界上最后一粒小麦的重量来衡量。

    让我们再次阅读文档:

    如果将可变对象添加到使用哈希值确定 对象位置 在集合中,[…]

    (强调我的)。

    文档的作者用他/她永恒的智慧来说,这意味着当你实现一个集合时,比如一本字典,你不应该使用散列来定位,因为这可能会改变。换言之,它与对可变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);
    }
    

    惊喜!

    因此,它们不使用指针相等,而是关注单词“may”,在本例中这意味着“may not”,并在向集合添加内容时简单地复制散列值。

    (所有这些实际上都很好,因为很难实现nshashmap或-hash,否则的话)。

        6
  •  -2
  •   Bombe    15 年前

    在爪哇,大多数可变类根本不重写。 Object.hashCode() 这样,默认实现将返回一个基于对象地址且不更改的值。目标C也可能是一样的。