簡體   English   中英

等於使用。 GetHashCode不是

[英]Equals is used. GetHashCode is not

我已經實現了以下課程:

public class carComparer : IEqualityComparer<Car>
    {
        public bool Equals(Car car1, Car car2)
        {
                if (car1 == null || car2 == null)
                    return false;

                return (car1.description == car2.description);
        }

        public int GetHashCode(Car car)
        {
            unchecked 
            {
                int hash = 17;
                hash = hash * 29 + car.id.GetHashCode();
                hash = hash * 29 + car.description.GetHashCode();
                return hash;
            }
        }

    }

現在看到這個:

Car p1 = new Car() { id = Guid.NewGuid(), description = "Test1" };
        Car p2 = new Car() { id = Guid.NewGuid(), description = "Test1" };
        Car p3 = new Car() { id = Guid.NewGuid(), description = "Test1" };
        Car p4 = new Car() { id = Guid.NewGuid(), description = "Test1" };
        var hash = new HashSet<Car>();
        hash.Add(p1);
        hash.Add(p2);

        var hash2 = new HashSet<Car>();
        hash2.Add(p3);
        hash2.Add(p4);

        var carComparer = new CarComparer();
        Assert.That(hash, Is.EquivalentTo(hash2).Using(carComparer));

我把斷點放在.equals和.hashcode中。 等於使用; 但GetHashCode不是。 為什么?

您正在使用NUnit Is.EquivalentTo比較兩個HashSet 它沒有理由調用GetHashCode - 它基本上比較了兩個集合的成員是否相等。 這就是為什么從不調用GetHashCode並調用Equals來比較來自不同HashSet的兩個項目的相等性。 您的哈希集也可以是列表或任何其他可枚舉的 - 在比較兩個集合時不會改變任何內容。

您可能希望在向HashSet添加項目時調用GetHashCode - 但事實並非如此,因為此時您的carComparer尚未知曉 - 您不會將其傳遞給HashSet構造函數。 如果你這樣做:

var hash = new HashSet<Car>(new carComparer());

然后在將新項添加到相應的HashSet時將調用GetHashCode

GetHashCode通常用於散列表查找。

GetHashCode不必保證唯一,因此不是有效的IsEqual測試。

對於要使用的GetHashCode,請使用HashSet的此構造函數:

https://msdn.microsoft.com/en-us/library/bb359100(v=vs.110).aspx

因此,為了使用GetHashCode方法,您需要使用:

var hash = new HashSet<Car>(carComparer);

請注意,將對象添加到哈希集時將驗證哈希。

然后在此調用中的HashSet.Add方法中使用Comparer:

private int InternalGetHashCode(T item)
{
  if ((object) item == null)
    return 0;

  //this._comparer is set during the constructor call
  return this._comparer.GetHashCode(item) & int.MaxValue;
}

由於顯而易見的原因,這使得Comparer成為只讀屬性。

所以,總結一下;

不使用GetHashCode ,因為它通常用於散列表查找創建,因此在開始添加項之前,您需要將其提供給HashSet。

由於顯而易見的原因使用IsEqual ; 如果不是:請參閱@dasblinkenlight的回答。

發生這種情況是因為IsEquivalent使用的算法決定了等價性:實現從您期望的集合構造它們所謂的“集合計數”對象,然后逐個嘗試從中刪除實際集合的項目:

public bool TryRemove(IEnumerable c) {
    foreach (object o in c)
        if (!TryRemove(o))
            return false;
    return true;
}

public bool TryRemove(object o) {
    for (int index = 0; index < list.Count; index++)
        if (ItemsEqual(list[index], o)) {
            list.RemoveAt(index);
            return true;
        }
    return false;
}

您可以看到NUnit使用效率相對較低的O(n 2 )算法,而不是為O(n)效率構造哈希集。 這對於較大的集合很重要,但由於單元測試中的典型集合只有少數項目,因此沒有明顯的差異。

ItemsEqual使用Equals比較器的Equals ,但它不需要哈希代碼功能( 源代碼 )。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM