HashMap put、get方法源码分析

HashMap.java的实现是面试必问的问题。

JDK版本

java version "1.8.0_91"
Java(TM) SE Runtime Environment (build 1.8.0_91-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.91-b15, mixed mode)

 

1. HashMap节点的封装

Node<K, V>

static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        V value;
        Node<K,V> next;
        //.....
}
    

hash值算法

public final int hashCode() {
            return Objects.hashCode(key) ^ Objects.hashCode(value);
        }

put null的key,计算hash值

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

ok,重点分析一下扩容

 1 /**
 2      * Initializes or doubles table size.  If null, allocates in
 3      * accord with initial capacity target held in field threshold.
 4      * Otherwise, because we are using power-of-two expansion, the
 5      * elements from each bin must either stay at same index, or move
 6      * with a power of two offset in the new table.
 7      *
 8      * @return the table
 9      */
10     final Node<K,V>[] resize() {
11         Node<K,V>[] oldTab = table;
12         int oldCap = (oldTab == null) ? 0 : oldTab.length;
13         int oldThr = threshold;
14         int newCap, newThr = 0;
15         if (oldCap > 0) {
16             if (oldCap >= MAXIMUM_CAPACITY) {
17                 threshold = Integer.MAX_VALUE;
18                 return oldTab;
19             }
20             else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
21                      oldCap >= DEFAULT_INITIAL_CAPACITY)
22                 newThr = oldThr << 1; // double threshold
23         }
24         else if (oldThr > 0) // initial capacity was placed in threshold
25             newCap = oldThr;
26         else {               // zero initial threshold signifies using defaults
27             newCap = DEFAULT_INITIAL_CAPACITY;
28             newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
29         }
30         if (newThr == 0) {
31             float ft = (float)newCap * loadFactor;
32             newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
33                       (int)ft : Integer.MAX_VALUE);
34         }
35         threshold = newThr;
36         @SuppressWarnings({"rawtypes","unchecked"})
37             Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
38         table = newTab;
39         if (oldTab != null) {
40             for (int j = 0; j < oldCap; ++j) {
41                 Node<K,V> e;
42                 if ((e = oldTab[j]) != null) {
43                     oldTab[j] = null;
44                     if (e.next == null)
45                         newTab[e.hash & (newCap - 1)] = e;
46                     else if (e instanceof TreeNode)
47                         ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
48                     else { // preserve order
49                         Node<K,V> loHead = null, loTail = null;
50                         Node<K,V> hiHead = null, hiTail = null;
51                         Node<K,V> next;
52                         do {
53                             next = e.next;
54                             if ((e.hash & oldCap) == 0) {
55                                 if (loTail == null)
56                                     loHead = e;
57                                 else
58                                     loTail.next = e;
59                                 loTail = e;
60                             }
61                             else {
62                                 if (hiTail == null)
63                                     hiHead = e;
64                                 else
65                                     hiTail.next = e;
66                                 hiTail = e;
67                             }
68                         } while ((e = next) != null);
69                         if (loTail != null) {
70                             loTail.next = null;
71                             newTab[j] = loHead;
72                         }
73                         if (hiTail != null) {
74                             hiTail.next = null;
75                             newTab[j + oldCap] = hiHead;
76                         }
77                     }
78                 }
79             }
80         }
81         return newTab;
82     }

首先说几点

1. 找tab位置,使用的key.hash() & (capacity - 1)

注意这里用的是位运算与操作,这里与的是capacity - 1,这样结果就不可能超过capactity - 1。不仅保证了结果正确,性能也提供了很多

2. 扩容大小为原来的大小左移一位,即扩大为原来的两倍

扩容主要的移动原来的元素,到新的数组里面

这里源码使用了比较好的方法,用key的hash值与capacity进行与,注意不是capacity - 1。如果与的结果是0,说明在新数组中的位置为原来的位置,不用变。如果结果不为0,最高为1,即capacty,说明在新数组中的位置为原来位置 + 原来的capacity

 

以前面试也被问道过,当时自信满满的胡说。自认为了解的很清楚,这里纠正一下

1. 封装的节点为Node,不是Entity

2. 扩容为大小为原来的两倍

3. 计算在数组位置,不是取余,是采用位操作。hashcode & capactiy - 1,注意这里的 - 1操作

4. put null的key,这里是可以的,null的hash值为0

5. key的计算为object.hashcode() ^ (object.hashcode >>> 16)

 

先到这

 p.s.https://tech.meituan.com/java-hashmap.html这个写的不错

posted on 2017-09-02 00:14  luckygxf  阅读(244)  评论(0编辑  收藏  举报

导航