2011-03-22 128 views
41

請澄清我在Hashset中的疑問。考慮下面的代碼,散列碼的散列碼和等於

class Person 
{ 
    String name; 

    Person(String n) 
    { 
     name=n; 
    } 
    public String getName() 
    { 
     return name; 
    } 

    @Override 
    public boolean equals(Object arg0) { 

     System.out.println("in equals"); 

     Person obj=(Person)arg0; 

     System.out.println("1st "+getName()); 
     System.out.println("2nd "+obj.getName()); 

     if(this.getName().equals(obj.getName())) 
     { 
       return true; 
     } 
     return false; 
    } 


    @Override 
    public int hashCode() { 

     System.out.println("in hash code"); 
     System.out.println(" value is "+Integer.valueOf(name.charAt(0))); 
     return Integer.valueOf(name.charAt(0)); 
    } 
} 

我有以下代碼

Person obj1=new Person("bcd"); 

Person obj2=new Person("cde"); 

Person obj3=new Person("abc"); 

Person obj4=new Person("abc"); 

現在,如果我添加這些對象的HashSet

Set<Person> sset=new HashSet<Person>(); 

sset.add(obj1); 
sset.add(obj4); 
sset.add(obj2); 
sset.add(obj3); 

我得到這個輸出

in hash code                  
value is 98  
in hash code 
value is 97  
in hash code  
value is 99  
in hash code  
value is 97 
in equals 
1st abc  
2nd abc 

問題1:爲什麼只有一次調用equals()函數才能檢查obj3和obj4?爲什麼它沒有檢查其餘的對象?

問題2:如果答案是因爲它們都具有相同的散列碼,纔等於將被調用,那麼爲什麼它不叫下面的代碼

sset.add(obj1); 
sset.add(obj4); 
sset.add(obj2); 
sset.add(obj4); 

輸出爲:

in hash code 
value is 98 
in hash code 
value is 97 
in hash code 
value is 99 
in hash code 
value is 97 

即使將兩個相同的對象添加到具有相同散列碼的散列集,它也不會進入equals()方法。

問題3:我迭代了上面的值並打印了內容,但沒有調用hashcode和equals。當它真的有用來重寫hashcode和equals方法?

問題4:何時會調用hashCode()equals()

+3

Mmh的......我不讀代碼格式化這個問題?...難道我格式化mayself明白了嗎?...沒有,它的後期, 我回家。 N8。 – Daniel 2011-03-22 19:49:43

+0

@保羅Ebermann:當你編輯請不要更改答案的意義 - 它是使用「的hashCode」的措辭,並在第2而不是「等於」故意。如果您不同意我的觀點,您可以自由撰寫新答案。 – Erik 2011-03-22 22:47:53

+0

@Erik:對不起,因爲這個問題是關於調用'.equals',我想答案應該是這個了。 (是的,你說得對,我應該補充註釋來代替。) – 2011-03-22 23:02:31

回答

46
  1. 如果hashCode不同,則無需致電equals
  2. 如果(obj1 == obj2)沒有必要致電hashCode
  3. 不需要hashCode和/或equals只是爲了迭代 - 你沒有比較對象
  4. 當需要區分對象時。
+2

'HashMap'實際計算哈希值來找到正確的桶和用''==或調用'等於()'比較對象的身份,然後使用它,但否則這是正確的。 'equals()'在第二個例子中沒有被調用,因爲'=='已經檢測到了重複。 – 2011-11-01 01:09:02

+4

有沒有必要的hashCode和/或等於只是迭代 - 你沒有比較的對象--- 那麼如何將它,如果它不叫迭代獲得哈希代碼當前桶? – 2012-06-20 06:21:30

16

我認爲你的問題都將被回答,如果你明白如何設置,特別是HashSets的工作。一個集合是獨特對象的集合,Java定義了唯一性,因爲它不等於其他任何東西(等於返回false)。

HashSet利用hashcodes來加快速度。它假定兩個彼此相等的對象將具有相同的散列碼。然而,它並不假定具有相同散列碼的兩個對象意味着它們是相等的。這就是爲什麼當它檢測到碰撞的哈希碼時,它只與具有相同哈希碼的集合中的其他對象(在你的情況下)相比。

-3

請所有的方法調試HashSet的,你會看到它是如何工作

2

因爲在第二種情況,你將相同的參考兩次,HashSet已覈對這HashMap.put()HashSet是基於:

 if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { 
      V oldValue = e.value; 
      e.value = value; 
      e.recordAccess(this); 
      return oldValue; 
     } 

正如你所看到的,只有在被添加的關鍵哈希equals將被稱爲等於鍵已經存在於集和這兩個引用是不同的。

11

根據從javasourcecode.org,HashSet的JDK源代碼中使用的HashMap作爲其內部實現方式中,約一個HashSet put方法的代碼如下:

public V put(K key, V value) { 
     if (key == null) 
      return putForNullKey(value); 
     int hash = hash(key.hashCode()); 
     int i = indexFor(hash, table.length); 
     for (Entry<K,V> e = table[i]; e != null; e = e.next) { 
      Object k; 
      if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { 
       V oldValue = e.value; 
       e.value = value; 
       e.recordAccess(this); 
       return oldValue; 
      } 
     } 

     modCount++; 
     addEntry(hash, key, value, i); 
     return null; 
    } 

規則首先檢查散列,然後檢查引用,然後調用等於物體的方法中的推杆。