经常在论坛上面看到覆写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{…}作为key放到HashMap。实际上HashMap也是把数据存在一个数组里面,所以在put函数里面,HashMap会调Foo.hashCode()算出作为这个元素在数组里面的下标,然后把key和value封装成一个对象放到数组。等一下,万一2个对象算出来的hash code一样怎么办?会不会冲掉?先回答第2个问题,会不会冲掉就要看Foo.equals()了,如果equals()也是true那就要冲掉了。万一是false,就是所谓的collision了。当2个元素hashCode一样但是equals为false的时候,那个HashMap里面的数组的这个元素就变成了链表。也就是hash code一样的元素在一个链表里面,链表的头在那个数组里面。
回过来说get的时候,HashMap也先调key.hashCode()算出数组下标,然后看equals如果是true就是找到了,所以就涉及了equals。
假设如果有个key为a的元素在HashMap里面的情况:
1:如果这时候用equals为true但是hashCode不等的b作为get参数的话,这个时候b算出来的数组下标一定不是a所在的下标位置。
2:如果这时候用equals为false但是hashCode相等的b作为get参数的话,这个时候b算出来的数组下标是对了,但是用equals来寻找相符的key就找不到a了。
以上2种情况要么就是get找不到符合的元素返回null,要么就是返回一个hashCode和equals恰好都符合b的另外的元素,这就产生了混乱。混乱的根本就是错误实现hashCode和equals。
下面有个非常简化版的HashMap实现帮助大家理解,您甚至可以把它当作伪代码来看。(这个实现只是为了模拟HashMap的机制,所以参数检查,访问修饰符都忽略。在java.util.HashMap里面的对hash值的二次hash,根据数组长度计算index,以及超出数组时的resize都忽略)
/*
* Just to demonstrate hash map mechanism,
* Please do not use it in your commercial product.
*
* Argument checking and modifier are ignored.
* In java.util.HashMap, array are used instead of ArrayList, so index of array is calculated by ‘h & (length-1)’,
* while we use ArrayList to skip the calculation for simple.
*
* @author Shengyuan Lu 卢声远
*/
public class SimpleHashMap {
ArrayList
/**
* 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
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
newEntry.add(new Entry(key, value));
entries.add(key.hashCode(), newEntry);
}
}
Object get(Object key) {
LinkedList
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:)
*/
}
[来源:http://blog.csdn.net/michaellufhl/archive/2010/08/23/5833188.aspx]
分类目录
- arch/management (17)
- computer (38)
- java/j2ee (305)
- lnmpa (237)
- mac/iphone/ipad/android (11)
- mysql/oracle/postgresql (126)
- os/software (74)
- other (518)
- python (6)
- redis/memcached/mongo (31)
- sitebuild (143)
随便看看
标签云
程序员 创业 人生箴言 eclipse 快捷键 术语 索引 unix命令 vim wordpress java学习笔记 环境变量 oracle内置函数 index 人生 数据类型 nohup tuxedo mysql学习笔记 MS-DOS命令 servlet spring 职场进阶 职业进阶 服务器选购 服务器选型 apache JPA MongoDB 注解 tomcat 子女教育 jquery maven JVM aix命令 网络营销 java异常 seo 人生规划 关键字 css 网络推广 struts 系统优化 成长 frame iframe bluehost jdbc select 我的信仰 oracle函数 cookie HashMap 站长工具 乱码 ArrayList secureCRT jsp session tail find halt 事务 oracle单记录函数 算法 URL window table javascript操作表单元素 String 字符串处理 健康 http 域名 情感 more google A记录 域名解析 netstat 弹出对话框 弹出窗口 框架集 框架 excel 字符串 javascript函数 showModalDialog nginx number 数组 sql frameset 开源程序 java数组 软件 oracle服务友情链接
收藏链接