浅谈hashCode
Posted cdlyy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅谈hashCode相关的知识,希望对你有一定的参考价值。
《Java编程思想》中对hashCode的描述:
设计hashCode()时最重要的因素就是:无论何时,对同一个对象调用hashCode()都应该产生同样的值。如果将一个对象用put()添加进HashMap时产生一个hashCdoe()值,而用get()取出时却产生了另一个hashCode()值,那么就无法重新取得该对象了。所以如果你的hashCode()方法依赖于对象中易变的数据,用户就要当心了,因为此数据发生变化时,hashCode()方法就会生成一个不同的散列码,相当于产生了一个不同的键。
1. HashCode源代码分析
int lockWord = shadow$_monitor_;
final int lockWordStateMask = 0xC0000000; // Top 2 bits.
final int lockWordStateHash = 0x80000000; // Top 2 bits are value 2 (kStateHash).
final int lockWordHashMask = 0x0FFFFFFF; // Low 28 bits.
if ((lockWord & lockWordStateMask) == lockWordStateHash) {
return lockWord & lockWordHashMask;
}
//返回的是对象引用地址
return System.identityHashCode(this);
}
int h = hash;
if (h == 0 && count > 0) {
for (int i = 0; i < count; i++) {
h = 31 * h + charAt(i);
}
hash = h;
}
return h;
}
//int值
return value;
}
2. HashCode带来的疑问
hashCode是什么?
hashCode作用?
hash code(hash值)是什么?
hash table(hash表)是什么?
hashCode方法对hash表有益处?
hashCode方法对不是hash有益处吗?
2.1什么是hash,hash值怎样生成的.
hash 翻译做“散列”,就是把任意长度的值输入,通过hash()函数输出固定长度的消息摘要。 hash函数也有很多种,包括:直接取余法,乘法取整法,平方取中法等等。
hash表是啥? hash表是由hash值组成的。
HashCode作用:
比如: hashcode有1,2,3,4,5,6,7,8个这样的位置。
HashCode的存在主要是为了查找的快捷性,HashCode是用来在散列存储结构中确定对象的存储地址的(用HashCode来代表对象就是在hash表中的位置).
如:存储1000个数,存储到了900,就先遍历900是否已经被存储,要遍历900次,如果没有就存储,这样会消耗很多时间。如果用hash存储,如hash表中有1,2,3,4,5,6,7,8个这样的位置.如果已经存储了100个数,当存储hashcode为1,那么就有20个数字和他的hashcode相同,只需要跟着20个数字相比较(使用equals),如果都不相同,就存储在1的位置。通过与原始方法的对比就知道hashcode具有很大的优势。
首先查看h(k2)输出值(内存地址),查看该内存地址是否存在值;
如果无,则表示该值不存在重复值;
如果有,则进行值比较,相同则表示该值已经存在散列列表中,如果不相同则再进行一个一个值比较;而无需一开始就一个一个值的比较,减少了查找次数
为什么这么说呢?考虑一种情况,当向集合中插入对象时,如何判别在集合中是否已经存在该对象了?(注意:集合中不允许重复的元素存在)
此时hashCode方法的作用就体现出来了,当集合要添加新的对象时,先调用这个对象的hashCode方法,得到对应的hashcode值,实际上在HashMap的具体实现中会用一个table保存已经存进去的对象的hashcode值,如果table中没有该hashcode值,它就可以直接存进去,不用再进行任何比较了;如果存在该hashcode值, 就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址,所以这里存在一个冲突解决的问题,这样一来实际调用equals方法的次数就大大降低了,说通俗一点:Java中的hashCode方法就是根据一定的规则将与对象相关的信息(比如对象的存储地址,对象的字段等)映射成一个数值,这个数值称作为散列值。下面这段代码是java.util.HashMap的中put方法的具体实现:
put方法是用来向HashMap中添加新的元素,从put方法的具体实现可知,会先调用hashCode方法得到该元素的hashCode值,然后查看table中是否存在该hashCode值,如果存在则调用equals方法重新确定是否存在该元素,如果存在,则更新value值,否则将新的元素添加到HashMap中。从这里可以看出,hashCode方法的存在是为了减少equals方法的调用次数,从而提高程序效率。
return putVal(hash(key), key, value, false, true);
}
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
也就是说对于两个对象,如果调用equals方法得到的结果为true,则两个对象的hashcode值必定相等;
如果两个对象的hashcode值不等,则equals方法得到的结果必定为false;
如果两个对象的hashcode值相等,则equals方法得到的结果未知。
显然,这个效率是相等低下的。散列表很好的解决了这个问题,它根据元素的散列码计算出元素在散列表中的位置,然后将元素插入该位置即可。对于相同的元素,自然是只保存了一个。
由此可知,若两个元素相等,它们的散列码一定相等;但反过来确不一定。在散列表中,
2、如果两个对象hashCode()相等,它们并不一定相等。
以上是关于浅谈hashCode的主要内容,如果未能解决你的问题,请参考以下文章