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 }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zydpfj.html