hashMap 源码注释分析(二)

接着上一篇继续

  1     /**
  2      * 返回map的长度
  3      * @return the number of key-value mappings in this map
  4      */
  5     public int size() {
  6         return size;
  7     }
  8 
  9     /**
 10      *如果map的长度为0 则返回true
 11      * @return <tt>true</tt> if this map contains no key-value mappings
 12      */
 13     public boolean isEmpty() {
 14         return size == 0;
 15     }
 16 
 17     /**
 18      * map的key 与value 都可以为 null
 19      * @see #put(Object, Object)
 20      */
 21     public V get(Object key) {
 22         Node<K,V> e;
 23         return (e = getNode(hash(key), key)) == null ? null : e.value;
 24     }
 25 
 26     /**
 27      * Implements Map.get and related methods
 28      *通过key值的hash值 与key 去查找Node
 29      * @param hash hash for key
 30      * @param key the key
 31      * @return the node, or null if none
 32      */
 33     final Node<K,V> getNode(int hash, Object key) {
 34         Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
 35         if ((tab = table) != null && (n = tab.length) > 0 &&
 36             (first = tab[(n - 1) & hash]) != null) {//将table 赋值给tab table不是 null ,table 长度大于0 该hash找到的hash桶Node不为null
 37             if (first.hash == hash && // always check first node//如果是一个元素直接判断
 38                 ((k = first.key) == key || (key != null && key.equals(k))))//判断找到的桶的hash值是否等于当前要找的hash值,key值是否是自己要找的key
 39                 return first;
 40             if ((e = first.next) != null) {//判断该哈希值是否有下一个元素
 41                 if (first instanceof TreeNode)//判断该hash桶是否是红黑树
 42                     return ((TreeNode<K,V>)first).getTreeNode(hash, key);//如果是红黑树已红黑树的方式查找
 43                 do {//不是红黑树就是链表,以链表的方式遍历查找。
 44                     if (e.hash == hash &&
 45                         ((k = e.key) == key || (key != null && key.equals(k))))
 46                         return e;
 47                 } while ((e = e.next) != null);
 48             }
 49         }
 50         return null;
 51     }
 52 
 53     /**
 54      * 判断是否包含某个key ,就是一个get 操作
 55      * @param   key   The key whose presence in this map is to be tested
 56      * @return <tt>true</tt> if this map contains a mapping for the specified
 57      * key.
 58      */
 59     public boolean containsKey(Object key) {
 60         return getNode(hash(key), key) != null;
 61     }
 62 
 63     /**
 64      * 将指定值与该映射中的指定键相关联。
 65      * 如果该映射先前包含该键的映射,则旧
 66      * 值被替换。
 67      *
 68      * @param key key with which the specified value is to be associated
 69      * @param value value to be associated with the specified key
 70      * @return the previous value associated with <tt>key</tt>, or
 71      *         <tt>null</tt> if there was no mapping for <tt>key</tt>.
 72      *         (A <tt>null</tt> return can also indicate that the map
 73      *         previously associated <tt>null</tt> with <tt>key</tt>.)
 74      */
 75     public V put(K key, V value) {
 76         return putVal(hash(key), key, value, false, true);
 77     }
 78 
 79     /**
 80      * Implements Map.put and related methods
 81      *
 82      * @param hash hash for key
 83      * @param key the key
 84      * @param value the value to put
 85      * @param onlyIfAbsent 如果 true, 不能修改覆盖当前的值
 86      * @param evict 如果 false, 当前table正在创建
 87      * @return previous value, or null if none
 88      */
 89     final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
 90                    boolean evict) {
 91         Node<K,V>[] tab; Node<K,V> p; int n, i;
 92         if ((tab = table) == null || (n = tab.length) == 0)//如果table 没有初始化
 93             n = (tab = resize()).length;//扩容操作
 94         if ((p = tab[i = (n - 1) & hash]) == null)//  (n - 1) & hash 映射到一个hash桶赋值给p  ,n的值为2的幂 转为二进制就是1000..减1 后就是11111111...
 95             //这样与hash进行& 操作时,就是111111111...与hash 之间 相同位置都为1才是1,这样就可以均匀分布到了hash桶中。
 96             tab[i] = newNode(hash, key, value, null);//创建第一个hash桶
 97         else {
 98             Node<K,V> e; K k;
 99             if (p.hash == hash &&
100                 ((k = p.key) == key || (key != null && key.equals(k))))//判断p 这个hash桶的hash值是否==当前的key
101                 e = p;
102             else if (p instanceof TreeNode)//如果hash桶是红黑树
103                 e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);//添加到树
104             else {//是链表
105                 for (int binCount = 0; ; ++binCount) {
106                     if ((e = p.next) == null) {//循环判断桶中是否有下一个元素
107                         p.next = newNode(hash, key, value, null);
108                         if (binCount >= TREEIFY_THRESHOLD - 1) //判断是否达到转换为红黑树的阈值
109                             treeifyBin(tab, hash);//转换为红黑树
110                         break;
111                     }
112                     if (e.hash == hash &&
113                         ((k = e.key) == key || (key != null && key.equals(k))))//如果链表中有该key值,则跳出
114                         break;
115                     p = e;
116                 }
117             }
118             if (e != null) { // existing mapping for key
119                 V oldValue = e.value;//把e的值赋值给 oldValue
120                 if (!onlyIfAbsent || oldValue == null)//如果onlyIfAbsent false 表示可以覆盖
121                     e.value = value;
122                 afterNodeAccess(e);
123                 return oldValue;
124             }
125         }
126         ++modCount;//操作数加1 ,相当于加了一个版本号
127         if (++size > threshold) //当size大于容器的扩容阈值
128             resize();//开始扩容
129         afterNodeInsertion(evict);//给LinkHashMap 使用的,在HashMap中是一个空方法
130         return null;
131     }
132 
133     /**
134      *HashMap的扩容操作,需要进行
135      * @return the table
136      */
137     final Node<K,V>[] resize() {
138         Node<K,V>[] oldTab = table;//把当前的table 容器备份给oldTab
139         int oldCap = (oldTab == null) ? 0 : oldTab.length;//计算容器的hash桶的数量
140         int oldThr = threshold;//把当前的容器扩容阈值记录
141         int newCap, newThr = 0;//初始化要创建的新生的容器的hash桶的数量与扩容阈值
142         if (oldCap > 0) { //表示当前容器不是初生的容器
143             if (oldCap >= MAXIMUM_CAPACITY) {//如果当前的hash桶的数量大于最大容量
144                 threshold = Integer.MAX_VALUE;//容器扩容阈值=最大的值
145                 return oldTab;
146             }
147             else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
148                      oldCap >= DEFAULT_INITIAL_CAPACITY) //如果当前的容器大小扩大1倍小于最大容量 或当前的容器的大小大于或等于初始化大小
149                 newThr = oldThr << 1; // double threshold 新的容器的扩容阈值是当前容器的2倍
150         }
151         //容器初次创建
152         else if (oldThr > 0) //当前的容器扩容阈值>0
153             newCap = oldThr;//容器的扩容阈值=当前的容器扩容阈值
154         else {               // 使用默认的设置
155             newCap = DEFAULT_INITIAL_CAPACITY;
156             newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
157         }
158 
159         //====================
160         if (newThr == 0) {//如果以上的操作后新的扩容阈值依然==0
161             float ft = (float)newCap * loadFactor; //计算新的扩容阈值
162             newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
163                       (int)ft : Integer.MAX_VALUE); //新的容器大小 小于最大容量值并且 新计算的ft < 最大容量值 ....
164         }
165         //开始干正事儿了.....
166         //扩容开始...~~~
167         threshold = newThr;//赋值容器扩容阈值
168         @SuppressWarnings({"rawtypes","unchecked"})
169             Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
170         table = newTab;//赋值新容器
171         if (oldTab != null) {//检查旧容器是否为空
172             for (int j = 0; j < oldCap; ++j) {//遍历旧的hash桶
173                 Node<K,V> e;
174                 if ((e = oldTab[j]) != null) {//判断hash桶中有数据 ,并备份
175                     oldTab[j] = null;
176                     if (e.next == null)//判断是否是普通数据
177                         newTab[e.hash & (newCap - 1)] = e;//放入新的hash桶中
178                     else if (e instanceof TreeNode)//判断是否是红黑树
179                         ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);//树重新放入hash桶
180                     else { // 剩下的一定是链表,用来解决jdk7中出现的扩容拷贝环问题。
181                         Node<K,V> loHead = null, loTail = null;// 低 定义头尾
182                         Node<K,V> hiHead = null, hiTail = null;// 高 定义头尾
183                         Node<K,V> next;
184                         /**
185                          * oldCap一定是2的整数次幂, 这里假设是2^m 要么是0 要么是2^m
186                          * newCap是oldCap的两倍, 则会是2^(m+1)
187                          * 那么进行与操作的时候是什么样的呢?
188                          * 例如:oldCap=8 ,e.hash=5(这个值随意)
189                          * 换为二进制为 :
190                          * oldCap:1000
191                          * e.hash:0101
192                          * 进行&操作会怎么样呢? 得到值为 oldCap 或 0,
193                          * 为什么会这样? 因为 oldCap只会在最高位上一位为 1 那结果只能是这两种情况
194                          * 那为什么要用这种方式来把原链表中的数据分成两个链表呢?
195                          * 那就分析一下HashMap的Hash 规则 (n - 1) & hash  ,其中 n 就是 newCap 的扩容阈值 也就是会是 2^(m+1)
196                          * 举个例子:
197                          * 当前的二进制分别为
198                          * oldCap:       01000
199                          * oldCap-1:     00111
200                          * newCap:       10000
201                          * newCap-1:     01111
202                          * e.hash:       00101
203                          *
204                          * 也就是说在 e.hash 没有改变的情况下 ,新的newCap-1比 oldCap-1 多了最高一位 1 那进行hash 计算后的情况 就只有两种 ,
205                          * 1、跟原来的hash相同 (这样就会原位不动)
206                          * 2、在新的容器中高位上多了一个1,那就是多了oldCap一倍  (这样就在原位置上乘以2)
207                          *
208                          * 那再分析一下这个结论跟使用 (e.hash & oldCap) == 0 的关系,那就是==0时, e.hash的关键一位(newCap-1比 oldCap-1 多了最高一位 1)是 0,而最高一位不是0的一定是1
209                          * 好了,分析完毕。
210                          */
211                         do {
212                             next = e.next;//备份
213                             if ((e.hash & oldCap) == 0) {//判断如果==0 不移位
214                                 if (loTail == null)//放到低位链表
215                                     loHead = e;
216                                 else
217                                     loTail.next = e;
218                                 loTail = e;
219                             }
220                             else {//否则 移位到高位
221                                 if (hiTail == null)//放到高位链表
222                                     hiHead = e;
223                                 else
224                                     hiTail.next = e;
225                                 hiTail = e;
226                             }
227                         } while ((e = next) != null);
228                         if (loTail != null) {
229                             loTail.next = null;
230                             newTab[j] = loHead;//原位不动
231                         }
232                         if (hiTail != null) {
233                             hiTail.next = null;
234                             newTab[j + oldCap] = hiHead;//位置加oldCap一倍
235                         }
236                     }
237                 }
238             }
239         }
240         return newTab;
241     }

超详细的HashMap 源码解读

posted @ 2019-11-28 17:33  你说累不累  阅读(269)  评论(0编辑  收藏  举报