为什么覆写equals的时候一定要覆写hashCode?

经常在论坛上面看到覆写hashCode函数的问题,很多情况下是一些开发者不了解hash code,或者和equals一起用的时候不太清楚为啥一定要覆写hashCode。


对于hash code的理论我不想多说,这个话题太大。我只想说用hash code的原因只有一个:效率。理论的说法它的复杂度只有O(1)试想我们把元素放在线性表里面,每次要找一个元素必须从头一个一个的找,复杂度有O(n)。如果放在平衡二叉树,复杂度也有O(log n)。


为啥很多地方说“覆写equals的时候一定要覆写hashCode”。
说到这里我知道很多人知道有个原则:
如果a.equals(b)那么要确保a.hashCode()==b.hashCode()。
为什么?hashCode和我写的程序的业务逻辑毫无关系,为啥我要override?
要我说如果你的class永远不可能放在hash code为基础的容器内,不必劳神,您真的不必override hashCode() :)

说得准确一点放在HashMap和Hashtable里面如果是作为value而不是作为key的话也是不必override hashCode了。至于HashSet,实际上它只是忽略value的HashMap,每次HashSet.add(o)其实就是HashMap.put(o, dummyObject)。

那为什么放到Hash容器里面要overide hashCode呢?
因为每次get的时候HashMap既要看equals是不是true也要看hash code是不是一致,put的时候也是要看equals和hash code。

 

如果说到这里您还是不太明白,咱就举个例子:

譬如把一个自己定义的class Foo{...}放到HashMap。实际上HashMap也是把数据存在一个数组里面,所以在put函数里面,HashMap会调Foo.hashCode()算出作为这个元素在数组里面的下标,然后把key和value封装成一个对象放到数组。
等一下,万一两个对象算出来的hash code一样怎么办?会不会冲掉?
先回答第2个问题,会不会冲掉就要看Foo.equals()了,如果equals()也是true那就要冲掉了。万一是false,就是所谓的collision了。当两个元素hashCode一样但是equals为false的时候,那个HashMap里面的数组的这个元素就变成了链表。也就是hash code一样的元素在一个链表里面,链表的头在那个数组里面。

回过来说get的时候,HashMap也先调key.hashCode()算出数组下标,然后看equals是不是true,所以就涉及了equals。


反观假设如果a.equals(b)但是a.hashCode()!=b.hashCode()的话,在put元素a之后,我们又用一个a.equals(b)但是b.hashCode()!=a.hashCode()的b元素作为key来get的时候就找不到a了。如果a.hashCode()==b.hashCode()但是!a.equals(b)倒是不要紧,这2个元素会collision然后被放到链表,只是效率变差。

这里有个非常简化版的HashMap实现帮助大家理解。

 

Java代码 复制代码
  1. /*  
  2. * Just to demonstrate hash map mechanism,   
  3. * Please do not use it in your commercial product. 
  4. *  
  5. */  
  6. public class SimpleHashMap {   
  7.     ArrayList<LinkedList<Entry>> entries = new ArrayList<LinkedList<Entry>>();   
  8.        
  9.     /**  
  10.     * Each key-value is encapsulated by Entry.  
  11.     */  
  12.     static class Entry {   
  13.         Object key;   
  14.         Object value;   
  15.         public Entry(Object key, Object value) {   
  16.             this.key = key;   
  17.             this.value = value;   
  18.        }   
  19.     }   
  20.        
  21.     void put(Object key, Object value) {   
  22.         LinkedList<Entry> e = entries.get(key.hashCode());   
  23.         if (e != null) {   
  24.             for (Entry entry : e) {   
  25.                 if (entry.key.equals(key)) {   
  26.                     entry.value = value;// Match in lined list  
  27.                     return;   
  28.                 }   
  29.             }   
  30.             e.addFirst(new Entry(key, value));// Add the entry to the list  
  31.         } else {   
  32.             // Put the new entry in array   
  33.             LinkedList<Entry> newEntry = new LinkedList<Entry>();   
  34.             newEntry.add(new Entry(key, value));   
  35.             entries.add(key.hashCode(), newEntry);   
  36.         }   
  37.     }   
  38.        
  39.     Object get(Object key) {   
  40.        LinkedList<Entry> e = entries.get(key.hashCode());   
  41.        if (e != null) {   
  42.             for (Entry entry : e) {   
  43.                 if (entry.key.equals(key)) {   
  44.                     return entry.value;   
  45.                 }   
  46.             }   
  47.        }   
  48.        return null;   
  49.     }   
  50.   
  51.     /**  
  52.     * Do we need to override equals() and hashCode() for SimpleHashMap itself?  
  53.     * I don't know either:)  
  54.     */  
  55. }  
/*
* Just to demonstrate hash map mechanism, 
* Please do not use it in your commercial product.
*
*/
public class SimpleHashMap {
	ArrayList<LinkedList<Entry>> entries = new ArrayList<LinkedList<Entry>>();
    
	/**
	* Each key-value is encapsulated by Entry.
	*/
	static class Entry {
		Object key;
		Object value;
		public Entry(Object key, Object value) {
			this.key = key;
			this.value = value;
	   }
	}
	
	void put(Object key, Object value) {
		LinkedList<Entry> e = entries.get(key.hashCode());
		if (e != null) {
			for (Entry entry : e) {
				if (entry.key.equals(key)) {
					entry.value = value;// Match in lined list
					return;
				}
			}
			e.addFirst(new Entry(key, value));// Add the entry to the list
		} else {
			// Put the new entry in array
			LinkedList<Entry> newEntry = new LinkedList<Entry>();
			newEntry.add(new Entry(key, value));
			entries.add(key.hashCode(), newEntry);
		}
	}
	
	Object get(Object key) {
	   LinkedList<Entry> e = entries.get(key.hashCode());
	   if (e != null) {
			for (Entry entry : e) {
				if (entry.key.equals(key)) {
					return entry.value;
				}
			}
	   }
	   return null;
	}

	/**
	* Do we need to override equals() and hashCode() for SimpleHashMap itself? 
	* I don't know either:)
	*/
}

 

这个问题的权威阐释可以参考Bloch的<Effective Java>的 Item 9: Always overridehashCode when you overrideequals

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值