关闭

HashMap源码之hash()函数分析(JDK 1.8)

标签: HashMap源码hash函数
6752人阅读 评论(5) 收藏 举报
分类:

转载请注明出处:http://blog.csdn.net/anxpp/article/details/51234835,谢谢!

    我们知道,使用散列的容器,其高性能的主要影响因素之一就是hash值。

    在HashMap中,为了更好的性能,我们希望作为Key的对象提供一个合理的hash函数以便能将其合理的分配到桶中。

    而在实际的HashMap中,对从对象获取的hash值又做了调整。

    我们先看源码:

  1. static final int hash(Object key) {
  2. int h;
  3. return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
  4. }

    我们可以将其详细步骤等价改为如下:

  1. static final int hash(Object key) {
  2. if(key == null)
  3. return 0;
  4. int h = key.hashCode();
  5. int temp = h >>> 16;
  6. int newHash = h ^ temp;
  7. return newHash;
  8. }

    代码过程的直接翻译就是:如果Key值为null,返回0;如果Key值不为空,返回原hash值和原hash值无符号右移16位的值按位异或的结果。

    我们知道,按位异或就是把两个数按二进制,相同就取0,不同就取1。

    比如:0101 ^ 1110 的结果为 1011。(记得以前上数字电路课的时候学过)异或的速度是非常快的。

    把一个数右移16位即丢弃低16为,就是任何小于216的数,右移16后结果都为0(2的16次方再右移刚好就是1)。

    任何一个数,与0按位异或的结果都是这个数本身(很好验证)。

    所以这个hash()函数对于非null的hash值,仅在其大于等于216的时候才会重新调整其值。

    但是调整后又什么好处呢?

    我们先看下put的时候,这个hash值是怎么处理(部分源码)的:

  1. public V put(K key, V value) {
  2. return putVal(hash(key), key, value, false, true);
  3. }
  4. final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
  5. Node<K,V>[] tab; Node<K,V> p; int n, i;
  6. if ((tab = table) == null || (n = tab.length) == 0)
  7. n = (tab = resize()).length;
  8. if ((p = tab[i = (n - 1) & hash]) == null)
  9. tab[i] = newNode(hash, key, value, null);
  10. ......
  11. }

    在寻找桶位的时候,这个hash值为与上table的zise-1,初始为16,我们就拿16来举例.

    以为算法是hashValue & size - 1 ,此时size-1=15的二进制为 1 1 1 1 ,也就是任意类似16进制0x?0(二进制最后四位为0000)的hash值,都会被存储到位置为0的桶位上,一个桶中的元素太多,就一定会降低其性能,但是我们来看看这样的hash值经过上面的函数处理过后的结果:

  1. public class TestHashCodeMethod {
  2. public static void main(String args[]) throws Exception{
  3. final int max = Integer.MAX_VALUE >>> 4;
  4. Random random = new Random(System.currentTimeMillis());
  5. for(int i=0;i<20;i++){
  6. int hash = random.nextInt(max) << 4;
  7. int betterHash = hash ^ (hash >>> 16);
  8. System.out.print(toBinaryString(hash));
  9. System.out.println("-->" + toBinaryString(betterHash));
  10. }
  11. }
  12. //将整数转换为二进制字符串,高位补0
  13. final static char[] digits = {'0','1'};
  14. static String toBinaryString(int i) {
  15. char[] buf = new char[32];
  16. int pos = 32;
  17. int mask = 1;
  18. do {
  19. buf[--pos] = digits[i & mask];
  20. i >>>= 1;
  21. } while (pos > 0);
  22. return new String(buf, pos, 32);
  23. }
  24. }

    查看结果该hash函数转换后的值:

  1. 00011000000100011111000101100000-->00011000000100011110100101110001
  2. 00110111100110001011010000100000-->00110111100110001000001110111000
  3. 01101110000011101111100011010000-->01101110000011101001011011011110
  4. 00000000000111110010101100010000-->00000000000111110010101100001111
  5. 00110101101001101000010010010000-->00110101101001101011000100110110
  6. 00101111111111001011000101010000-->00101111111111001001111010101100
  7. 01100101111101101110100110110000-->01100101111101101000110001000110
  8. 00000011101101101110000110100000-->00000011101101101110001000010110
  9. 00100011001101010011110010110000-->00100011001101010001111110000101
  10. 01101101111010000001111011110000-->01101101111010000111001100011000
  11. 01111001111100110101000101010000-->01111001111100110010100010100011
  12. 00111110101001111101100110100000-->00111110101001111110011100000111
  13. 01011001000001101010011001110000-->01011001000001101111111101110110
  14. 01101000101100101110101100100000-->01101000101100101000001110010010
  15. 01100110111011001111110001000000-->01100110111011001001101010101100
  16. 00100001100011000010110001100000-->00100001100011000000110111101100
  17. 01100010001010000110101111110000-->01100010001010000000100111011000
  18. 00000011001111101111111110110000-->00000011001111101111110010001110
  19. 00111110100100101011111110110000-->00111110100100101000000100100010
  20. 01000100000101011111111110000000-->01000100000101011011101110010101

    是不是发现情况都发生了好转,原来一大批会被放到“0”桶位的hash值,现在几乎都被更佳合理的分配到了其他桶位。

    我们知道hashMap中的桶位都是以oldCap<<1(即原容量*2)来增长的,所以最终这个hash值要存放的时候,都是跟一连串二进制的“1"作与运算的,而容量定义为int类型,java中int类型为4字节,即32位,但是Integer.MAX为0x7fffffff,也就是231 - 1 那么大(因为最高位被用作符号位),而取16算是一种折衷的办法。而另一个原因,也许是跟对象本身的hash值(当然也为int)有关。

    那么这个方法就介绍这么多了,近期准备将HashMap整个源码解读一下,并分享出来,并在最终整体介绍一下Java的容器体系。

    之前已发过两篇容器的源码解读,这里给出链接:

    Java之LinkedList源码解读(JDK 1.8)

    Java之ArrayList源码解读(JDK 1.8)

9
0
查看评论
发表评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场

Java 集合深入理解(16):HashMap 主要特点和关键方法源码解读

>点击查看 Java 集合框架深入理解 系列, - ( ゜- ゜)つロ 乾杯~ 前面我们介绍了 哈希相关概念:哈希 哈希函数 冲突解决 哈希表,这篇文章我们来根据 JDK 1.8 源码,深入了解...
  • u011240877
  • u011240877
  • 2016-11-26 14:36
  • 6200

Hash算法以及java hashmap的源码分析

hash算法
  • lizhi20091225
  • lizhi20091225
  • 2013-08-21 17:32
  • 1511

jdk1.8 HashMap源码分析(resize函数)

final Node[] resize() { Node[] oldTab = table; int oldCap = (oldTab == null) ? 0 : o...
  • mymilkbottles
  • mymilkbottles
  • 2017-08-02 13:52
  • 142

HashMap源码分析(jdk1.8)

HashMap源码前前后后看了好几次,也和同事分享过好几次,每次都有新的收获。 分享也是一种提高! 本文首写于个人云笔记(点击访问),经多次修改,短期内不会有重大修改了,现发于此,有任何问题欢迎交流指...
  • u010887744
  • u010887744
  • 2015-12-17 20:24
  • 9543

HashMap源码分析(JDK1.8)- 你该知道的都在这里了

HashMap是Java和Android程序员的基本功, JDK1.8对HashMap进行了优化, 你真正理解它了吗? 考虑如下问题:  1、哈希基本原理?(答:散列表、hash碰...
  • brycegao321
  • brycegao321
  • 2016-09-13 16:40
  • 1752

Java面试绕不开的问题: Java中HashMap底层实现原理(JDK1.8)源码分析

Java面试绕不开的问题: Java中HashMap底层实现原理(JDK1.8)源码分析
  • baidu_37107022
  • baidu_37107022
  • 2017-07-28 19:57
  • 425

HashMap 源码分析 基于 JDK 1.8

长文慎入,HashMap 的源码分析和整体类层结构分析。UML 类图不知道大家还记不记得在 ArrayList 那篇文章中,我谈到说不定存在 AbstractSet、AbstractMap 等抽象类的...
  • chen_kkw
  • chen_kkw
  • 2017-12-19 17:38
  • 33

Java中HashMap底层实现原理(JDK1.8)源码分析

这几天学习了HashMap的底层实现,但是发现好几个版本的,代码不一,而且看了Android包的HashMap和JDK中的HashMap的也不是一样,原来他们没有指定JDK版本,很多文章都是旧版本JD...
  • ocean_fan
  • ocean_fan
  • 2017-11-19 17:19
  • 182

HashMap源码分析——JDK1.8

JDK1.8的HashMap部分源码分析
  • u013329334
  • u013329334
  • 2016-03-23 17:34
  • 265

JDK 1.8之 HashMap 源码分析

转载请注明出处:http://blog.csdn.net/crazy1235/article/details/75579654构造函数 Node hash put treeifyBin get res...
  • crazy1235
  • crazy1235
  • 2017-07-21 00:57
  • 1065
    个人资料
    • 访问:747921次
    • 积分:5460
    • 等级:
    • 排名:第5713名
    • 原创:96篇
    • 转载:1篇
    • 译文:0篇
    • 评论:291条
    博客专栏
    其他信息