用C#实现GetHashCode.Null值处理

本文关键字:处理 Null GetHashCode 实现 | 更新日期: 2023-09-27 18:01:06

在我开始之前,这里的所有代码样本都在Mono环境中进行了测试,GetHashCode实现中有一个明显的区别:

string.Empty.GetHashCode(); // returns 0 in Mono 3.10
string.Empty.GetHashCode(); // returns 757602046 in .NET 4.5.1

我是基于@JonSkeet的这个SO Answer实现的,在评论中,他还建议对NULL值使用0哈希代码(不确定我应该如何对它们进行哈希(。

我通常使用0作为null的有效哈希代码,这与忽略字段不同。

因此有以下实现(Mono 3.10(:

public class Entity {
    public int EntityID { get; set; }
    public string EntityName { get; set; }
    public override int GetHashCode() {
        unchecked {
            int hash = 15485863;       // prime number
            int multiplier = 1299709;  // another prime number
            hash = hash * multiplier + EntityID.GetHashCode();
            hash = hash * multiplier + (EntityName != null ? EntityName.GetHashCode() : 0);
            return hash;
        }
    }
}

很容易发现碰撞,例如

var hash1 = new Entity { EntityID = 1337, EntityName = "" }.GetHashCode();
var hash2 = new Entity { EntityID = 1337, EntityName = null }.GetHashCode();
bool equals = hash1 == hash2; // true

我可以用其他数字替换null值0,但这不会解决问题,因为仍然有可能一些哈希(字符串(输出会生成这样的数字,我会遇到另一个冲突。

我的问题是:在使用上面例子中的算法时,我应该如何处理空值?

用C#实现GetHashCode.Null值处理

您的"问题"是您正在尝试获得无冲突的哈希代码。虽然这在大多数情况下非常适合使用哈希码进行查找的集合实现(例如HashSetDictionary(的查找性能,但这将不起作用。

原因是哈希码只是一个32位的整数值,它代表的数据通常要大得多(多个整数值、字符串等(

所以散列码只是用来定义两个对象可以相等。集合类使用哈希代码来细化存储对象的区域,并使用equals函数来查找两个对象是否真的相同。因此,您应该始终为实现哈希代码的类实现Equals函数。虽然这些类将返回到对象的equals函数,但实现IEquatable<T>接口以避免任何类型的键入问题(仍然覆盖对象的默认equals方法!(

也是一个好主意吗

我的问题:在使用算法时应该如何处理空值从上面的例子?

我认为问题不在于null本身。问题在于你使用GetHashCode来表示平等,而这并不是为了平等。CCD_ 8应该提供这种渴望正态分布的散列。

医生说:

两个相等的对象返回相等的哈希代码。然而相反:相等的哈希码并不意味着对象相等,因为不同(不相等(的对象可以具有相同的哈希代码

然后详细说明GetHashCode:的用途

哈希代码用于中的高效插入和查找基于哈希表的集合

您应该实现IEquatable<Entity>,在这里您实际上定义了两个实体的等价关系。并且当你在!===时覆盖它

近似值:

public class Entity : IEquatable<Entity>
{
    public int EntityId { get; set; }
    public string EntityName { get; set; }
    public bool Equals(Entity other)
    {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return EntityId == other.EntityId && 
               string.Equals(EntityName, other.EntityName);
    }
    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != this.GetType()) return false;
        return Equals((Entity) obj);
    }
    public static bool operator ==(Entity left, Entity right)
    {
        return Equals(left, right);
    }
    public static bool operator !=(Entity left, Entity right)
    {
        return !Equals(left, right);
    }
    public override int GetHashCode()
    {
        unchecked
        {
            return (EntityId*397) ^ (EntityName != null ? EntityName.GetHashCode() : 0);
        }
    }
}