在c#中散列数组

时间:2016-05-09 14:15:56

标签: c# .net arrays hash

简短问题

如何为GetHashCode实施Array

详情

我有一个覆盖Equals的对象,检查:

this.array[n] == otherObject.array[n]

适用于n中的所有array

当然,我应该实施补充GetHashCode。 我想知道是否有.NET方法可以做到这一点,或者我是否应该实现自己的方式,比如

hash = hash ^ array[n]

澄清

我的对象包含一个数组,我对GetHashCode感兴趣的数组元素。我的数组等价代码仅作为示例 - 就像我的问题所说,但也许我不清楚,我对GetHashCode感兴趣(不是Equals)。我说我自然应该实现补充GetHashCode,因为一旦Equals被覆盖(Dictionary等正常运行),实现这一点就需要.NET。感谢。

4 个答案:

答案 0 :(得分:7)

要使用数组元素计算哈希代码,可以将数组转换为IStructuralEquatable,然后调用GetHashCode(IEqualityComparer)方法,为数组中的元素类型传递比较器。 / p>

(强制转换是必要的,因为Array类显式实现了该方法。)

例如,如果您的对象具有int数组,那么您可以像这样实现GetHashCode:

public override int GetHashCode()
{
    return ((IStructuralEquatable)this.array).GetHashCode(EqualityComparer<int>.Default);
}

如果你有点好奇,这里是Array类如何实现GetHashCode方法(来自Reference Source):

internal static int CombineHashCodes(int h1, int h2) {
    return (((h1 << 5) + h1) ^ h2);
}

int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) {
    if (comparer == null)
        throw new ArgumentNullException("comparer");
    Contract.EndContractBlock();

    int ret = 0;

    for (int i = (this.Length >= 8 ? this.Length - 8 : 0); i < this.Length; i++) {
        ret = CombineHashCodes(ret, comparer.GetHashCode(GetValue(i)));
    }

    return ret;
}

如您所见,当前实现仅使用数组的最后八个元素。

答案 1 :(得分:4)

我不同意您应该自然地在阵列上实现GetHashCode 您必须在每次更改时更新它 或者在飞行中计算 我会在飞行中直接比较
SequenceEquals将使用默认的相等比较器,因此您还应该实现

public bool Equals

>

中的对象

Enumerable.SequenceEqual
有一个例子

public static void SequenceEqualEx1()
{
    Pet pet1 = new Pet { Name = "Turbo", Age = 2 };
    Pet pet2 = new Pet { Name = "Peanut", Age = 8 };

    // Create two lists of pets.
    List<Pet> pets1 = new List<Pet> { pet1, pet2 };
    List<Pet> pets2 = new List<Pet> { pet1, pet2 };

    bool equal = pets1.SequenceEqual(pets2);

    Console.WriteLine(
        "The lists {0} equal.",
        equal ? "are" : "are not");
}

答案 2 :(得分:1)

这取决于您想要...

Michael上面回答的一个选择是基于数组元素具有哈希码。这将符合您的Equals值语义。但是,由于“作为准则,对象的散列在对象的整个生命周期中必须相同”,因此,您必须确保数组在获得其散列码后不发生变化。拥有一个永远不变的需求的不变容器,这对我来说很容易出错。

您的另一个(IMO更好的选择)是切换到不可变容器(即ImmutableArray),然后基于值的哈希码才有意义。您可以如上所述使用IStructuralEquatable,也可以更普遍地使用:

    public override GetHashCode() {
        int ret = 0, index = 0, startIndex = collection.Count >= 8 ? collection.Count - 8 : 0;
        foreach (var v in Value) if (index >= startIndex) ret = HashCode.Combine(ret, v.GetHashCode());
        return ret;
    }

同样适用于其他不可变集合。

答案 3 :(得分:0)

使用当前框架,可以考虑使用

int value=0;
for (var i = 0;i< this.array.Length; i++)
{
    value=HashCode.Combine(this.array[i],value);
}