解析HashMap源码

发布时间 2023-08-03 04:15:43作者: Yblue

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底层结构是 由 数组+链表+红黑树 组成