散列码的哈希码和相等
请在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");
现在,如果我将这些对象添加到哈希集
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 :为什么调用obj3和obj4只调用一次equals()函数? 为什么没有检查其余的对象?
问题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()方法内部。
问题4 :我迭代了上面的值并打印了内容,但是hashcode和equals都不被调用。 当其真正有用的重写hashcode和equals方法?
问题3 :当hash代码和equals会被调用?
- 如果
hashCode
不同,则不需要调用equals - 没有必要调用
hashCode
if(obj1 == obj2)
- 不需要
hashCode
和/或等于迭代 – 你不比较对象 - 当需要区分对象时。
我想你的问题都将被回答,如果你明白如何设置,特别是HashSets工作。 一个集合是唯一对象的集合,Java定义唯一性,因为它不等于其他任何东西(等于返回false)。
HashSet利用hashcodes来加快速度。 它假定两个彼此相等的对象将具有相同的哈希码。 但是,它并不假定具有相同散列码的两个对象意味着它们是相等的。 这就是为什么当它检测到一个相冲突的散列码时,它只会与具有相同散列码的集合中的其他对象(在你的情况下)进行比较。
根据javasourcecode.org的jdk源代码,HashSet使用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; }
规则是首先检查哈希,然后检查引用,然后调用对象的equals方法。
你应该阅读如何确保你已经正确地实现了equals和hashCode。 这是一个很好的起点: 在Java中重写equals和hashCode时应该考虑哪些问题?
因为在第二种情况下,两次添加相同的引用,并且HashSet
在HashSet
所基于的HashMap.put()
检查:
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; }
正如你所看到的,仅当被添加的密钥的哈希值等于已经存在于集合中的密钥并且这两者的引用不同时才会调用equals。
请用所有的方法debuggingHashSet,你会看到它是如何工作的