2011-03-22 89 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; 
    } 

规则首先检查散列,然后检查引用,然后调用等于物体的方法中的推杆。