转自:http://blog.csdn.net/michaellufhl/archive/2010/08/23/5833188.aspx
public boolean equals(Object obj)
public int 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实现帮助大家理解。
/*
* Just to demonstrate hash map mechanism,
* Please do not use it in your commercial product.
*
* @author Shengyuan Lu 卢声远 <michaellufhl@yahoo.com.cn>
*/
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 override hashCode when you override equals
分享到:
相关推荐
主要介绍了Java重写equals及hashcode方法流程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
(3) 重载的时候,方法名要一样,但是参数类型和个数不一样,返回值类型可以相同也可以不相同。 无法以返回值类型作为重载函数的区分标准。 复制代码 /** * 1. 方法重载只可以通过方法名和方法参数来...
行业分类-设备装置-一种数据覆写方法
主要介绍了Java的覆写操作,结合实例形式分析了java属性覆写及super调用父类方法覆写相关操作技巧,需要的朋友可以参考下
Spring Cloud 覆写远端的配置属性
Android 自定义view,覆写onMeasure例子
覆写
覆写Feign的默认配置-代码部分-代码部分.zip 覆写Feign的默认配置-代码部分博文中,优化后的服务消费者和服务提供者,以及Eureka Server、父级工程的源代码
硬盘客体重用的安全等级保护覆写机制研究,清除
以下页面在运行中,调试输出的永远是alter('1'),原因是按照页面加载从上至下的顺序,同名的方法以最后一个为准.
Linux内核漏洞利用技术:覆写modprobe_path 安全架构 安全架构 业务风控 系统安全 APT
基于覆写验证的云数据确定性删除方案
PNW入门之got表覆写-附件资源
主要介绍了Spring Cloud 覆写远端的配置属性的相关知识,非常不错,具有参考借鉴价值,需要的朋友可以参考下
/**一个类实现了两个接口,两个接口中的方法相同,这个类在覆写的时候就会出现冲突*/ class D: B,C{ //当下面两个方法同时存在的时候,就会报方法相同的冲突 override fun x(): Int { return super
西门子之在处理脚本中的字符串时应该注意什么?pdf,西门子之在处理脚本中的字符串时应该注意什么? 下面说明了关于在C脚本中处理字符串时最重要的问题。必须考虑性能和稳定性因素。在不一致时,内存区可能被覆写,...
==和 equals ()都是用于...所以,通常override(重写/覆写)java.lang.0bject 类的中equals()方法 按照自己的需要,在equals()方法中定义对象相等的含义。 String.equals () 注意:当此方法被重写时,通常有必要重写ha