码迷,mamicode.com
首页 > 其他好文 > 详细

关于HashMap的一些研究

时间:2017-10-10 19:16:47      阅读:221      评论:0      收藏:0      [点我收藏+]

标签:获取   span   png   equal   直接插入   assert   imu   ppi   查找   

  关于扩容,在resize()方法中,一般是扩容一倍  newCap = oldCap << 1. 扩容的同时,若原table中存在元素,则需要将原table中的元素进行重新计算哈希桶位置.

  在设置初始值的时候,需要将容器大小设置为最接近2次幂的值,例如new HashMap<>(5);则初始容器大小为8.源码为:

 1 //Returns a power of two size for the given target capacity.
 2 static final int tableSizeFor(int cap) {
 3     int n = cap - 1;
 4     n |= n >>> 1;
 5     n |= n >>> 2;
 6     n |= n >>> 4;
 7     n |= n >>> 8;
 8     n |= n >>> 16;
 9     return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
10 }

  为什么HashMap的容量一定要设置为2次幂呢? 方便put和get. 一般获取位置使用取余运算%,h % length即可得到哈希值所在的位置,而HashMap中由于容量为2次幂,它使用的是

h & (length - 1),取余运算m % n可以等效于m - m / n * n,位运算比取余运算快了不少.在jdk1.7中,存在方法indexFor用于计算哈希值的位置

1 /**
2  * Returns index for hash code h.
3  */
4 static int indexFor(int h, int length) {
5     // assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2";
6     return h & (length-1);
7 } 

而在jdk1.8中,该方法已经不存在了,直接使用在各个方法中,例如 putVal中  p = tab[i = (n - 1) & hash]

  关于哈希冲突,由于 h & (n - 1) 会得到同一个位置的值,就会造成冲突.当冲突小时,HashMap会将该桶转换成一个链表,每个桶的元素为:

1  static class Node<K,V> implements Map.Entry<K,V> {
2         final int hash;
3         final K key;
4         V value;
5         Node<K,V> next;
6         //省略其他代码
7 }    

可以看出当next没值时,就没有哈希冲突,当next有值时,该桶即为一个链表结构.当冲突高于某个界限(这个界限为TREEIFY_THRESHOLD = 8)时,该桶将转变为一颗红黑树. 为什么要转换成红黑树呢?  当存在哈希冲突时,对链表的查找复杂度为 O(N),是线性的,当冲突较大时,查找会较慢,此时转换成红黑树,红黑树的查找复杂度为O(logN),比链表快.源代码如下:

 1 //是否冲突,不冲突直接插入到该桶
 2 if ((p = tab[i = (n - 1) & hash]) == null)
 3     tab[i] = newNode(hash, key, value, null);
 4 else {
 5   //存在冲突
 6    Node<K,V> e; K k;
 7    if (p.hash == hash &&
 8      ((k = p.key) == key || (key != null && key.equals(k))))
 9        e = p; //key的hash相等以及值也相等,则覆盖
10    else if (p instanceof TreeNode)
11        e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); //已经是颗红黑树,则往树里插入元素
12    else { //链表
13        for (int binCount = 0; ; ++binCount) {
14             if ((e = p.next) == null) {
15           //将桶转变成链表
16               p.next = newNode(hash, key, value, null);
17               if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
18            //大于界限,转换成红黑树
19                   treeifyBin(tab, hash);
20                   break;
21              }
22              if (e.hash == hash &&
23                 ((k = e.key) == key || (key != null && key.equals(k))))
24                 break;
25             p = e;
26          }
27      }
28      if (e != null) { // existing mapping for key
29         V oldValue = e.value;
30         if (!onlyIfAbsent || oldValue == null)
31             e.value = value;
32             afterNodeAccess(e);
33             return oldValue;
34      }
35 }

 

哈希冲突时很常见的

当存在不规范的代码,即对象hashcode相等当时equals不相等时,该对象当key必存在冲突.

 1         Map<String,String> map = new HashMap<>();
 2         map.put("a","a");
 3         map.put("b","a");
 4         map.put("c","a");
 5         map.put("d","a");
 6         map.put("e","a");
 7         map.put("f","a");
 8         map.put("g","a");
 9         map.put("r","a");
10         map.put("s","a");
11         map.put("t","a");
12         map.put("w","a");
13         map.put("x","a");
14         map.put("y","a");
15         map.put("z","a");    

在上面这段代码中,跟踪程序,当执行到14行时,由于存在了13个元素,大于16*0.75,此时扩容,跟踪HashMap中的table

技术分享

扩容后重新计算元素的哈希桶位置后

技术分享

原先为9个桶13个元素,重新计算后得到13个桶13个元素,可以判断出此时已经存在哈希冲突. 进一步跟踪,可以得到冲突的桶:

b -> a.next = r -> a
c -> a.next = s -> a
d -> a.next = t -> a
g -> a.next = w -> a

关于HashMap的一些研究

标签:获取   span   png   equal   直接插入   assert   imu   ppi   查找   

原文地址:http://www.cnblogs.com/joels/p/7646795.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!