hash - 仍在可变对象的哈希码上

标签 hash hashtable hashcode

我知道,我知道有很多关于哈希码的问题,但我想就计算可变对象哈希码的几个解决方案发表意见。

从这个假设开始(documentation):

In general, for mutable reference types, you should override GetHashCode only if:

  • You can compute the hash code from fields that are not mutable; or
  • You can ensure that the hash code of a mutable object does not change while the object is contained in a collection that relies on its hash code.

Otherwise, you might think that the mutable object is lost in the hash table.

当我需要将可变对象存储到哈希表中时,哪个是最佳选择?

解决方案 1

忽略这个问题。计算是否使用可用算法之一(此处和 C# 中的地理坐标示例):

public override Int32 GetHashCode() {
    Int32 n1 = 99999997;
    Int32 hash_lat = this.Latitude.GetHashCode() % n1;
    Int32 hash_lng = this.Longitude.GetHashCode();
    _final_hashcode = (((hash_lat << 5) + hash_lat) ^ hash_lng);
    return _final_hashcode.Value;
}

解决方案 2

第一次计算可变值并存储它以供下次使用:

private Int32? _final_hashcode = null;
public override Int32 GetHashCode() {
    // hash code must not change when lat and lng does change
    if (_final_hashcode == null) {
        Int32 n1 = 99999997;
        Int32 hash_lat = this.Latitude.GetHashCode() % n1;
        Int32 hash_lng = this.Longitude.GetHashCode();
        _final_hashcode = (((hash_lat << 5) + hash_lat) ^ hash_lng);
    }
    return _final_hashcode.Value;
}

解决方案 3

为对象添加一个不可变的私有(private) key ,仅用于哈希码。这样,当可变字段发生变化时,哈希码不会发生变化。

这里是一个使用随机生成的私有(private) GUID 的示例,该 GUID 不是类所必需的,仅用于哈希码:

public class GeoPosition {

    private const Guid _guidForHash = Guid.NewGuid(); // init during contruction

    public override Int32 GetHashCode() {
        return _guidForHash.GetHashCode();
    }

    // mutable properties here and other stuff
    // ...
}

你怎么看?

最佳答案

这很简单:

  • 解决方案 2:如果您有对象 o1 和 o2,并且它们具有不同的字段值,则它们具有不同的哈希码。如果您随后更改这些对象的字段以使它们彼此相等,它们仍然不会具有相同的哈希码。它打破了约束:o1 == o2 implies hash(o1) == hash(o2)。不可行的解决方案。

  • 解决方案 3:与 2 相同的问题。

  • 解决方案 1:一个正确的哈希函数,但每次都需要重新计算哈希码。

所以解决方案 1 是。如果您需要对其进行优化(请记住,过早优化是万恶之源),您可以缓存哈希码并在每次写入属性后对其进行更新:

private Int32 UpdateHashCode() {
        Int32 n1 = 99999997;
        Int32 hash_lat = this.Latitude.GetHashCode() % n1;
        Int32 hash_lng = this.Longitude.GetHashCode();
        cached_hashcode = (((hash_lat << 5) + hash_lat) ^ hash_lng);
}
private Int32 cached_hashcode = null;
public override Int32 GetHashCode() {
    if (cached_hashcode == null) {
        UpdateHashCode();
    }
    return cached_hashcode.Value;
}
private string latitude;
public string Latitude {
    set {
        latitude = value;
        UpdateHashCode();
    }
}
private string longitude;
public string Longitude {
    set {
        longitude = value;
        UpdateHashCode();
    }
}

关于hash - 仍在可变对象的哈希码上,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33099663/

相关文章:

c - 开放寻址冲突解决

Java:将重复的对象添加到集合中?

java - Java 对象的多个 HashCode

JAVA:哈希码:是否有可能多个元素具有相同的哈希码?

java - 通过 Java 访问 GPU 进行加盐哈希

python - 更有效的数据结构/算法在数据库中查找相似的图像哈希

java - 如何改进 HashMap 数组中的搜索

python - python中的确定性递归散列

powershell - 根据列表替换 PSObj 属性值

java - 在哈希表中存储单词组