Java HashMap分析之三:放入元素

开发 后端
现在,有了hash code,来考虑如何计算放入数组的位置。hash code值通常会很大,但是数组的大小有限,默认只有16,大的也不能超过2的30次方。所以,用模运算来保证在数组大小范围内是合理的,比如:index = hash code % array size.不过这有点慢,JDK采用了更快的算法。

现在,有了hash code,来考虑如何计算放入数组的位置。hash code值通常会很大,但是数组的大小有限,默认只有16,大的也不能超过2的30次方。所以,用模运算来保证在数组大小范围内是合理的,比如:index = hash code % array size.不过这有点慢,JDK采用了更快的算法。这个更快的算法源于一个数学规律,就是如果size是2的N次方,那么数X对size的模运算结果等价于X和size-1的按位与运算,也就是 X % size <=> X & (size -1).按位与只消耗一个CPU周期,当然快多了。现在就可理解为什么要故意把数组大小弄成2的N次方了。再回头看一开始计算数组大小的代码,完全理解了。

  1. int capacity = 1;  
  2.         while (capacity < initialCapacity)  
  3.             capacity <<= 1

比如size=16,二进制表示如下:(32位)

0000000000000000000000000010000

size-1=15,表示如下:

0000000000000000000000000001111

假如hash code=4

0000000000000000000000000000100

4 & 15 结果为:

0000000000000000000000000000100

假如hash code=6

0000000000000000000000000000101

6 & 15 结果为:

0000000000000000000000000000101

假如hash code=38

0000000000000000000000000100110

38 & 15 结果为:

0000000000000000000000000000110

通过观察这三个例子,又可以发现一个特点,也就是X & size-1 的结果受到了size的阶数的限制,这里size=16,阶数为4.结果就是只用低4位的1和X按位与,而X的高位没有用到。这会导致重复率相当高。如果用一个算法将X的低位重新计算,比如根据所有位的值进行重新计算,就可以使得hash值分布更均匀。下面的代码揭示了在真正按位与之前,调用了hash函数,进行了一堆位运算。至于为什么用这个算法,我也不知道其来历。

  1. public V put(K key, V value) {  
  2.         if (key == null)  
  3.             return putForNullKey(value);  
  4.         int hash = hash(key.hashCode());  
  5.         int i = indexFor(hash, table.length);  
  6.         for (Entry<K,V> e = table[i]; e != null; e = e.next) {  
  7.             Object k;  
  8.             if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {  
  9.                 V oldValue = e.value;  
  10.                 e.value = value;  
  11.                 e.recordAccess(this);  
  12.                 return oldValue;  
  13.             }  
  14.         }  
  15.  
  16.         modCount++;  
  17.         addEntry(hash, key, value, i);  
  18.         return null;  
  19.     }  
  20.  
  21.     static int hash(int h) {  
  22.         // This function ensures that hashCodes that differ only by  
  23.         // constant multiples at each bit position have a bounded  
  24.         // number of collisions (approximately 8 at default load factor).  
  25.         h ^= (h >>> 20) ^ (h >>> 12);  
  26.         return h ^ (h >>> 7) ^ (h >>> 4);  
  27.     }  
  28.  
  29.     static int indexFor(int h, int length) {  
  30.         return h & (length-1);  
  31.     }  
  32.  
  33.     void addEntry(int hash, K key, V value, int bucketIndex) {  
  34.         Entry<K,V> e = table[bucketIndex];  
  35.         table[bucketIndex] = new Entry<K,V>(hash, key, value, e);  
  36.         if (size++ >= threshold)  
  37.             resize(2 * table.length);  
  38.     } 

上面的for循环是查找并替换符合条件的对象,如果找不到,则添加新的对象。查找到的条件(必须都满足)是:

1.hash值相等

2.key的引用相同或者key的值相等。

原文链接:http://blog.csdn.net/sheismylife/article/details/7355282

【编辑推荐】

  1. Java HashMap分析之二:Hash code
  2. Java HashMap分析之一:基本结构
  3. Java集合框架总结:Set接口的使用
  4. Java的位移运算巧方法
  5. Java7的一个新类JLayer:装饰的Swing组件
责任编辑:林师授 来源: sheismylife的博客
相关推荐

2015-08-10 15:12:27

Java实例源码分析

2016-09-12 14:33:20

javaHashMap

2012-03-15 16:27:13

JavaHashMap

2012-03-15 16:12:57

JavaHashMap

2022-03-31 16:26:49

鸿蒙源码分析进程管理

2012-02-15 10:37:38

JavaJava Socket

2012-05-03 11:35:56

ApacheCXFJava

2015-10-30 15:30:54

LevelDBSSTableSybase

2023-10-07 08:05:17

数据分析模型行为分析

2019-07-30 12:36:10

云计算微软亚马逊

2021-02-22 14:04:47

Vue框架项目

2021-02-04 07:22:07

NPOI操作Excel

2017-06-01 22:59:45

Akka层次结构Actors

2019-09-28 23:17:41

zabbix运维监控

2011-06-24 16:26:20

SEO

2019-06-03 05:00:00

物联网边缘计算IOT

2011-04-12 14:43:08

XML

2013-05-28 09:33:47

虚拟化虚拟化存储

2021-10-29 09:44:50

C++指针变量

2022-02-17 21:28:08

AbilityJSFA鸿蒙
点赞
收藏

51CTO技术栈公众号