解析HashMap源码
1.运算知识补充:
// >>> 有符号右移 运算 最高位符号位也会移动 // 计算机中,负数是以对应补码形式存放的 // int v = -1; //-1对应的补码形式 1111111 1111111 1111111 1111111 -1 // System.out.println((v >>> 24));//0000000 0000000 0000000 1111111 255 // System.out.println((v >>> 16));//0000000 0000000 1111111 1111111 65535 // System.out.println((v >>> 8));// 0000000 1111111 1111111 1111111 16777215 // System.out.println((v >>> 0));// 1111111 1111111 1111111 1111111 -1 // ^ 异或 运算,在两个数 二进制 进行运算中,如果两个相应的位相同,则运算结果为0,否则1 // 例如: // 15 ^ 8 ; // 运算结果为:7; // 15 的二进制为: 1111 // 8 的二进制位: 1000 // 按位比较的结果为:0111 System.out.println(15 ^ 8); // & 与 运算,在两个数 二进制 进行运算中,都是1结果为1,其他都是0 // 例如: // 15 & 8 ; // 运算结果为:8; // 15 的二进制为: 1111 // 8 的二进制位: 1000 // 按位比较的结果为:1000 System.out.println(15 & 8);
2.HashMap源码
Map map = new HashMap<>(); //1.默认初始化大小是16 /** * Constructs an empty <tt>HashMap</tt> with the default initial capacity * (16) and the default load factor (0.75). */ public HashMap() { this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted }
//2.调用 put 方法 public V put(K key, V value) { return putVal(hash(key), key, value, false, true); }
//3.hash方法 static final int hash(Object key) { int h; //首先会调用key所在类的hasCode的hash值,然后hash值位运算和异或运算后赋值回去 // key的hash值 hash值(位运算)向右移16位 return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); // 异或运算 }
//4.putVal方法 final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { HashMap.Node<K,V>[] tab; HashMap.Node<K,V> p; int n, i; // 如果map还是空的,则先开始初始化,table是map中用于存放索引的表 if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; // hash值与数组长度减一的值进行 与运算 得到分散的数组位置 if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); //若这个位置为空,则添加成功 else {//若这个位置非空 HashMap.Node<K,V> e; K k; if (p.hash == hash && //当前key的hash值与该位置的运算后的hash相同 ((k = p.key) == key || (key != null && key.equals(k)))) //且当前key.equals(该位置的key)为true e = p;//则 将当前节点保存在 临时节点 e //若当前节点p是(红黑)树类型的节点 else if (p instanceof HashMap.TreeNode) e = ((HashMap.TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); else { // 链表 TREEIFY_THRESHOLD=8 for (int binCount = 0; ; ++binCount) { if ((e = p.next) == null) { p.next = newNode(hash, key, value, null); if (binCount >= TREEIFY_THRESHOLD - 1) // 若链表节点满 8 treeifyBin(tab, hash);//且总节点达到64个 转红黑树(看方法treeifyBin() MIN_TREEIFY_CAPACITY=64 ) break; } if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e; } } if (e != null) { // existing mapping for key V oldValue = e.value; //onlyIfAbsent 默认为false,所以这个值一定被替换 if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; } } ++modCount; //扩容2倍 if (++size > threshold) resize(); afterNodeInsertion(evict); return null; } }
3.总结
总结:调用key所在类的hasCode的hash值且通过一定运算(位运算,异或运算,与运算)找到数组上的一个位置, 若当前位置为空,则添加成功 若非空,则与当前位置的key的hash值比较(一定运算后的), 若不同,则添加成功 若相同,则的调用key的equals(当前位置的key)方法, 若值为true,则替换当前位置key的value值, 若值为false,则添加成功 若一链表的节点达到8个,且总节点达到64,则转为红黑树 HashMap底层结构是 由 数组+链表+红黑树 组成