簡體   English   中英

為什么在添加到集合時不會為所有對象調用Equals()

[英]Why is Equals() being not called for the all objects while adding to collection

我有一個類型,我在IDictionary中使用它作為鍵。 類型如下

public  class Employee
{
    public string Name { get; set; }
    public int ID { get; set; }

    public override bool Equals(object obj)
    {
        Employee emp = obj as Employee;
        if (emp != null)
            return emp.Name.Equals(this.Name);
        return false;
    }

    public override int GetHashCode()
    {
        return this.Name.GetHashCode();
    }
}

現在我已經在我的主要內容中創建了一個字典,如下所示

 IDictionary<Employee, int> empCollection = new Dictionary<Employee, int>();
        Employee emp1 = new Employee() { Name = "abhi", ID = 1 };
        Employee emp2 = new Employee() { Name = "vikram", ID = 2 };
        Employee emp3 = new Employee() { Name = "vikram", ID = 3 };

        empCollection.Add(emp1, 1);
        empCollection.Add(emp2, 2);
        empCollection.Add(emp3, 3);

現在在調試時我發現當emp1被添加到集合中時,只調用了GetHashCode方法的密鑰類型,之后當emp2被添加到集合中時,只會再次調用GetHashCode方法,但是在emp3的情況下,GetHashCode和Equals方法都是叫做。

可能在問這個問題時看起來太天真但是為什么當eqImp2對象被添加到集合時不會調用Equals方法。 里面發生了什么。 請解釋。

字典和所有其他類似容器使用哈希碼作為快速和臟檢查:不同的哈希碼意味着兩個對象絕對不相等; 相同的哈希碼並不意味着什么。 GetHashCode的文檔通過說明來指定此行為

如果兩個對象比較相等,則每個對象的GetHashCode方法必須返回相同的值。 但是,如果兩個對象的比較不相等,則兩個對象的GetHashCode方法不必返回不同的值。

你的emp1emp2生成不同的哈希碼,所以字典不需要運行Equals ; 它已經知道它們並不平等。 另一方面, emp2emp3生成相同的哈希碼,因此字典必須調用Equals以確定它們是否確實相等,或者相同的哈希碼只是偶然的結果。

在您的示例中, GetHashCode查看Name哈希碼。 emp3與emp2同名,(“vikram”)。 它們在給定哈希碼的情況下是相等的,因此它使用Equals進一步查看。

emp2和emp3具有相同的密鑰。 這將導致字典中的“鍵沖突”。 它首先調用GetHashCode()並確定哈希碼是相同的。 然后通過調用Equals()確保它們是相同的。 Dictionary中的代碼是:

int num = this.comparer.GetHashCode(key) & 2147483647;
...
if (this.entries[i].hashCode == num && this.comparer.Equals(this.entries[i].key, key))

顯然,如果哈希碼不匹配,則永遠不必調用Equals。

您應該獲得像ILSpy這樣的工具,然后您可以查看代碼並自己找到答案。

如果繼續此實驗,您將觀察到一些特定於Dictionary<TKey, TValue>實現的行為,以及由於您實現GetHashCode的方式而需要的一些行為。

首先,在比較對象的相等性時,了解GetHashCodeEquals的作用非常重要。 有關此問題的其他信息,但我將在重復基本規則:

  1. Equals方法確切地確定哪些對象相等以及哪些對象不相等。 在返回之前,需要在此方法中執行所有必要的檢查以進行最終確定。
  2. 哈希碼是根據對象的值計算的值。 通常它比原始對象小得多(在我們的例子中,哈希碼是4字節整數)並且不一定是唯一的。 但是,與原始對象本身相比,計算和比較要快得多。
    • 當哈希碼不需要是唯一的時,不同的哈希碼表示不同的對象(即Equals肯定會返回false),但是相等的哈希碼並不意味着什么(即Equals可以返回true或false)。

將值與關鍵對象相關聯的集合(例如,.NET中的IDictionary<TKey, TValue>或Java中的Map<K, V> )利用哈希碼來提高實現效率。 但是,由於Object.GetHashCode的文檔特別不要求結果是唯一的,因此這些集合不能單獨依賴哈希代碼來獲得正確的功能。 當兩個對象具有相同的哈希碼時,只有對Equals的調用才能區分它們。 您為插入emp3描述的情況屬於這種情況:如果您嘗試插入相同的值,[ IDictionary<TKey, TValue>.Add ]方法需要拋出ArgumentException ,並且只有對Equals的調用才能確定是否新密鑰emp3等於先前插入的emp3

其他實施特征

特定的集合實現可能導致對GetHashCode調用超出預期。 例如,當調整散列表的內部存儲大小時,實現可能會為集合中存儲的每個對象調用GetHashCode 根據集合二進制B樹可能只調用GetHashCode一次(如果結果是在樹結構中緩存),或者可能需要調用GetHashCode每次插入或查找操作過程中對多個對象(如果結果沒有被高速緩存) 。

有時哈希表實現需要為多個對象調用GetHashCode ,或者甚至可能為具有不同哈希碼的對象調用Equals ,因為他們使用模運算將鍵放入“桶”中。 其具體特征因實施而異。

這是因為GetHashCode是一種快捷方式。 C#將首先調用GetHashCode ,它應該是快速執行的。 如果兩個對象具有不同的HashCodes,那么就不需要調用更為昂貴的Equals方法。 只有當它們具有相同的HashCode時,它才會調用Equals 這是因為HashCode不能保證是唯一的

暫無
暫無

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

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