hashCode和equals详解
来源:互联网 发布:李昌钰蓝可儿 知乎 编辑:程序博客网 时间:2024/05/15 14:11
最近在重温java源码,一边看一边总结下,并且分享下自己的心得,共同学习,欢迎指点。这一篇说下hashCode和equals的源码中的一些注意点,争取把原理讲透彻:
源码
平时用hashMap多会用到get,put,iterator等方法,在这些方法里面都能发现hashCode()和equals的身影,它是生成hashMap的key的重要步骤,所以在这里做下深入并延伸下。
public native int hashCode();这是Object中的方法,可以发现是个本地方法。但是许多类都会覆盖这个方法比如String,Integer,Long等基础类。所以不同的类有不同的散列方式,如Integer类只是单纯的返回了value;
public int hashCode() {return value; }
而String则会遍历每个字符,并且做【h = 31*h + val[off++]】这样一个运算,h=0,value为char数组。
public int hashCode() {int h = hash; int len = count;if (h == 0 && len > 0) { int off = offset; char val[] = value; for (int i = 0; i < len; i++) { h = 31*h + val[off++]; } hash = h; } return h; }
而默认的本地方法实现方式是./hotspot/src/share/vm/runtime/synchronizer.cpp +530 get_next_hash方法
static inline intptr_t get_next_hash(Thread * Self, oop obj) { intptr_t value = 0 ; if (hashCode == 0) { value = os::random() ; } else if (hashCode == 1) { intptr_t addrBits = intptr_t(obj) >> 3 ; value = addrBits ^ (addrBits >> 5) ^ GVars.stwRandom ; } else if (hashCode == 2) { value = 1 ; // for sensitivity testing } else if (hashCode == 3) { value = ++GVars.hcSequence ; } else if (hashCode == 4) { value = intptr_t(obj) ; } else { unsigned t = Self->_hashStateX ; t ^= (t << 11) ; Self->_hashStateX = Self->_hashStateY ; Self->_hashStateY = Self->_hashStateZ ; Self->_hashStateZ = Self->_hashStateW ; unsigned v = Self->_hashStateW ; v = (v ^ (v >> 19)) ^ (t ^ (t >> 8)) ; Self->_hashStateW = v ; value = v ; } value &= markOopDesc::hash_mask; if (value == 0) value = 0xBAD ; assert (value != markOopDesc::no_hash, "invariant") ; TEVENT (hashCode: GENERATE) ; return value;}根据hashCode的不同情况做不同处理,如1的时候返回随机数,2则返回跟地址有关的一个数值。具体逻辑含义需要仔细看下jvm源码。
public boolean equals(Object obj) {return (this == obj);}这是equals的源码,没有特殊,判断地址。
注意项:
1:一个好的hash算法和一个差的hash算法相差很多,好的hash算法可以使HashMap的get无限趋于O(1),而差的hash算法可以无限趋于O(n)。
好的hash算法为不同的对象产生不同的hashCode。
2:覆盖equals时候的约定:
自反性:对于任何非null的引用值x,x.equals(x)必须返回true。
对称行:对于非空x,y 当且仅当y.equals(x)返回true时,x.equals(y)必须返回true
传递性:对于任何非null的引用x,y,z,如果x.equals(y)返回true,并且y.equals(z)也返回true,那么x.equals(z)也必须返回true
一致性:如果对于非空x,y,并且没有修改,多次调用x.equals(y)必须相等
对于任何非null的x,x.equals(null)必须返回false
3:当覆盖equals时总要覆盖hashCode
考虑如下代码
public class MyInt {public int value ;MyInt(int value){this.value=value; }public boolean equals(Object o){if(this == o)return true;if(!(o instanceof MyInt))return false;MyInt mi = (MyInt)o;return (this.value == mi.value);}}
假设此时你想用这个类作为key与hashmap一起使用:
Map<MyInt,String> map = new HashMap<MyInt,String>();map.put(new MyInt(2), "test");System.out.println(map.get(new MyInt(2)));如果你期望返回test那么就错了。你可能以为重写了equals方法,用hashmap就会对应到一个key上。不会的 。hashmap用的是hashcode进行散列的。两个不一样的对象在hashCode看来是不一样的。如果hashMap的hashCode恰巧也相等了,好吧恭喜你,中奖了。
e.hash == hash && ((k = e.key) == key || key.equals(k))
hashMap中的判断条件是这样的,因为MyInt重写了equals,又恰巧hashCode也相同,所以可以返回。但是千万不要抱这种侥幸心里。
所以通过以上分析,在重写equals方法后一定要重写HashCode
4:总结性的,没有特别必要的情况不要重写equals方法;如果一定要重写equals方法,那么一定要重写hashCode方法,并且要找一个效率高的hash算法。如果你不是一个大牛可以考虑看下《effective java》,里面介绍了一种比较通用的效率也很高的算法。如果是大牛,求分享算法。
0 0
- equals() 和hashcode()详解
- hashcode()和equals()详解
- hashCode和equals详解
- equals和hashCode方法详解
- equals和hashCode方法详解
- equals和hashCode方法详解
- equals和hashCode方法详解
- java中的hashcode()和equals()方法详解
- java中hashcode()和equals()的详解
- java中hashcode()和equals()的详解
- java中hashcode()和equals()的详解
- equals方法和hashCode方法详解
- java中hashcode()和equals()的详解
- java中hashcode()和equals()的详解
- java中hashcode()和equals()的详解
- java中hashcode()和equals()的详解
- java中hashcode()和equals()的详解
- Java中的hashcode()和equals()方法详解
- 如何强制谷歌浏览器 使用 美国域名搜索
- UvaLive 3403 Mobile Computing 枚举二叉树
- 机器学习中的相似性度量
- JBPM下载地址
- 翻译 mock.patch注释
- hashCode和equals详解
- java reference
- Hibernate中多对多的annotation的写法(中间表可以有多个字段)
- 【BZOJ3876】【Ahoi2014】支线剧情 有下界的最小费用最大流
- 20070215-Costas arrays
- java中的null注意事件总结
- 数据结构实验七——循环队列
- 关于“could not deserialize”错误的思考
- Oracle索引块分裂split信息汇总