HashMap源码

0.底层数据结构
在 JDK1.7 中,由“数组+链表”组成,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的。

在 JDK1.8 中,由“数组+链表+红黑树”组成。当链表过长,则会严重影响 HashMap 的性能,红黑树搜索时间复杂度是 O(logn),而链表是糟糕的 O(n)。因此,JDK1.8 对数据结构做了进一步的优化,引入了红黑树,链表和红黑树在达到一定条件会进行转换:

当链表长度超过 8 且数据总量大于等于 64 才会转红黑树。
将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树,以减少搜索时间
1.默认容量

    /**
     * The default initial capacity - MUST be a power of two.
     */
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
   

2.加载因子

    /**
  * The load factor used when none specified in constructor.
  */
 static final float DEFAULT_LOAD_FACTOR = 0.75f;

考虑预计的entry数在map及其负载系数,并且尽量减少rehash操作的次数 0.75是对空间和时间效率的一个平衡。

3.node


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

      Node(int hash, K key, V value, Node<K,V> next) {
          this.hash = hash;
          this.key = key;
          this.value = value;
          this.next = next;
      }
    

4.扩容,位运算高效

 static final int tableSizeFor(int cap) {
        int n = cap - 1;
        n |= n >>> 1;
        n |= n >>> 2;
        n |= n >>> 4;
        n |= n >>> 8;
        n |= n >>> 16;
        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;

5.散列算法
1.7

final int hash(Object k) {
   int h = hashSeed;
   if (0 != h && k instanceof String) {
       return sun.misc.Hashing.stringHash32((String) k);
   }

   h ^= k.hashCode();//以下为扰动运算,扰动了 4 次
   h ^= (h >>> 20) ^ (h >>> 12);//
   return h ^ (h >>> 7) ^ (h >>> 4);//
}

static int indexFor(int h, int length) {
   return h & (length-1);
}

1.8

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

6.链表改为红黑树

for (int binCount = 0; ; ++binCount) {
    if ((e = p.next) == null) {
        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
        break;
    }
    if (e.hash == hash &&
        ((k = e.key) == key || (key != null && key.equals(k))))
        break;
    p = e;
}
  1. 1.7扩容(头插法会产生环,线程不安全,另一方面put多线程不安全)
    void resize(int newCapacity) {
        Entry[] oldTable = table;
        int oldCapacity = oldTable.length;
        if (oldCapacity == MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return;
        }
 
        Entry[] newTable = new Entry[newCapacity];
        transfer(newTable, initHashSeedAsNeeded(newCapacity));
        table = newTable;
        threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
    }
    /**
    * Transfers all entries from current table to newTable.
    */
    void transfer(Entry[] newTable, boolean rehash) {
       int newCapacity = newTable.length;
       for (Entry<K,V> e : table) {
           while(null != e) {
               Entry<K,V> next = e.next;              //注释1
               if (rehash) {
                   e.hash = null == e.key ? 0 : hash(e.key);
               }
               int i = indexFor(e.hash, newCapacity); //注释2
               e.next = newTable[i];                  //注释3
               newTable[i] = e;                       //注释4
               e = next;                              //注释5
           }
       }
   }
  1. 1.8扩容尾插法
 final Node<K,V>[] resize() {
       Node<K,V>[] oldTab = table;
       int oldCap = (oldTab == null) ? 0 : oldTab.length;
       int oldThr = threshold;
       int newCap, newThr = 0;
       if (oldCap > 0) {
           if (oldCap >= MAXIMUM_CAPACITY) {
               threshold = Integer.MAX_VALUE;
               return oldTab;
           }
           else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                    oldCap >= DEFAULT_INITIAL_CAPACITY)                      //注释1
               newThr = oldThr << 1; // double threshold
       }
       else if (oldThr > 0) // initial capacity was placed in threshold
           newCap = oldThr;
       else {               // zero initial threshold signifies using defaults
           newCap = DEFAULT_INITIAL_CAPACITY;
           newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
       }
       if (newThr == 0) {
           float ft = (float)newCap * loadFactor;
           newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                     (int)ft : Integer.MAX_VALUE);
       }
       threshold = newThr;
       @SuppressWarnings({"rawtypes","unchecked"})
           Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
       table = newTab;
       if (oldTab != null) {
           for (int j = 0; j < oldCap; ++j) {                                 //注释2
               Node<K,V> e;
               if ((e = oldTab[j]) != null) {
                   oldTab[j] = null;
                   if (e.next == null)                                        //注释3
                       newTab[e.hash & (newCap - 1)] = e;
                   else if (e instanceof TreeNode)
                       ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                   else { // preserve order
                       Node<K,V> loHead = null, loTail = null;
                       Node<K,V> hiHead = null, hiTail = null;
                       Node<K,V> next;
                       do {
                           next = e.next;
                           if ((e.hash & oldCap) == 0) {                      //注释4
                               if (loTail == null)                            //注释5
                                   loHead = e;
                               else
                                   loTail.next = e;                           //注释6
                               loTail = e;                                    //注释7
                           }
                           else {
                               if (hiTail == null)
                                   hiHead = e;
                               else
                                   hiTail.next = e;
                               hiTail = e;
                           }
                       } while ((e = next) != null);
                       if (loTail != null) {                                  /注释8
                           loTail.next = null;
                           newTab[j] = loHead;
                       }
                       if (hiTail != null) {
                           hiTail.next = null;
                           newTab[j + oldCap] = hiHead;
                       }
                   }
               }
           }
       }
       return newTab;
   }
   ```
9. 1.7put,get
```java
public V get(Object key) {
   // 判断key 是否为null
   
    if (key == null)
     	// 如果为null 获取key为null的值
         return getForNullKey();
     // 获取对应key为要查询的key的entry
     Entry<K,V> entry = getEntry(key);
     // 判断是否获取到entry,如果没有,返回null,如果不为null,返回对应entry的value值
     return null == entry ? null : entry.getValue();
 }

 // 当key为null时获取value的值
 private V getForNullKey() {
 	// 判断hashmap中总的entry的数量,如果为0,说明hashmap中还没有值,返回null
     if (size == 0) {
         return null;
     }
     // 如果size 不为0 , 获取entry[] 数组中 下标为0的位置的链表
     for (Entry<K,V> e = table[0]; e != null; e = e.next) {
     	// 如果有entry对应的key的值为null ,返回对应的value
         if (e.key == null)
             return e.value;
     }
   	// 如果没有,返回空
     return null;
 }

 // 如果key不为null,获取key对应的value
 final Entry<K,V> getEntry(Object key) {
 	// 如果key不为null,判断hashmap中entry的数量是否为0 如果为0 返回null
     if (size == 0) {
         return null;
     }

     // 获取key的value值,如果key为null,返回hash值为0,反之,计算key对应的hash值
     int hash = (key == null) ? 0 : hash(key);
     // 遍历指定下标的entry数组元素链表
     for (Entry<K,V> e = table[indexFor(hash, table.length)];
          e != null;
          e = e.next) {
         Object k;
     	// 判断key的hash值与entry中的hash值是否相同,并且key通过== 和 equal 比较,
     	// 都为true时,返回这个 entry 对象 
         if (e.hash == hash &&
             ((k = e.key) == key || (key != null && key.equals(k))))
             return e;
     }
     // 如果指定下标key中的entry没有满足条件的,返回null
     return null;
 }

 // 计算 hash值
 final int hash(Object k) {
     int h = hashSeed;
     if (0 != h && k instanceof String) {
         return sun.misc.Hashing.stringHash32((String) k);
     }
     h ^= k.hashCode();
     h ^= (h >>> 20) ^ (h >>> 12);
     return h ^ (h >>> 7) ^ (h >>> 4);
 }

 // 通过hash值以及数组长度的位运算,获取entry的下标
 static int indexFor(int h, int length) {
     // assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2";
     return h & (length-1);
 }
public V put(K key, V value) {
    // 判断 entry[] 数组是否为空数组 如果为空 初始化entry数组
    if (table == EMPTY_TABLE) {
    	// 初始化hashmap
        inflateTable(threshold);
    }
    // 如果key 为null 把这个value对应的entry放进table[0]位置中
    if (key == null)
        return putForNullKey(value);
    // 计算 key的hash值
    int hash = hash(key);
    // 计算 key对应的 entry所在数组的下标
    int i = indexFor(hash, table.length);
    // 获取上面计算的下标的链表
    for (Entry<K,V> e = table[i]; e != null; e = e.next) {
        Object k;
        // 如果存在hash值相同,并且key相同的entry,对value进行覆盖,并返回覆盖区的value
        if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
            V oldValue = e.value;
            e.value = value;
            e.recordAccess(this);
            return oldValue;
        }
    }

    // 如果链表中没有,则添加
    modCount++;
    addEntry(hash, key, value, i);
    return null;
	}


	// 初始化 hashmap 
    private void inflateTable(int toSize) {
        // 根据初始化的值,获取对应的小的大于这个值的 2 的n次方的值,也就是hashmap的容量
        int capacity = roundUpToPowerOf2(toSize);
        // 通过容量与扩容因子的相乘,获取最大不触发扩容的容量
        threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
        // 创建数组
        table = new Entry[capacity];
        initHashSeedAsNeeded(capacity);
    }

    // 获取当前数值最接近并且大于当前数值的最小2的n次方
	private static int roundUpToPowerOf2(int number) {
        // Integer.highestOneBit((number - 1) << 1) 获取当前数值减去1后向左位移1位,并且第二位往后都为0的值
        return number >= MAXIMUM_CAPACITY
                ? MAXIMUM_CAPACITY
                : (number > 1) ? Integer.highestOneBit((number - 1) << 1) : 1;
    }

    // 当key为null的时候,对应value存放的位置
    private V putForNullKey(V value) {
    	// 获取table[0]的entry, 遍历这个链表
        for (Entry<K,V> e = table[0]; e != null; e = e.next) {
        	// 如果存在key为null的entry,把value进行覆盖,并返回覆盖前的value
            if (e.key == null) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
        // 如果对应key为null的entry不存在,则在table[0]位置添加一个key为null的entry,并modcount加1
        modCount++;
        // 添加entry
        addEntry(0, null, value, 0);
        // 返回null
        return null;
    }
    // 添加 entry 参数是key的hash值,key,value,下标
    void addEntry(int hash, K key, V value, int bucketIndex) {
    	// 判断hashmap中所有entry的数量是否大于扩容临界值并且指定下标处的entry[]数组元素不为null 触发扩容
        if ((size >= threshold) && (null != table[bucketIndex])) {
        	// 扩容后的容量是原先的两倍
            resize(2 * table.length);
            // 获取key的hash值
            hash = (null != key) ? hash(key) : 0;
            // 重新计算下标
            bucketIndex = indexFor(hash, table.length);
        }
        // 创建entry
        createEntry(hash, key, value, bucketIndex);
    }

    // 创建entry的方法
    void createEntry(int hash, K key, V value, int bucketIndex) {
    	// 获取指定下标的entry
        Entry<K,V> e = table[bucketIndex];
        // 新创建的entry作为原先链表的最顶端,覆盖创建前的entry
        table[bucketIndex] = new Entry<>(hash, key, value, e);
        // 数组内总的entry加1
        size++;
    }

    // 扩容算法
    void resize(int newCapacity) {
    	// 首先把老的数组复制到一个临时数组中
        Entry[] oldTable = table;
        // 保存老的数组的长度
        int oldCapacity = oldTable.length;
        // 判断 老的数组长度是否等于最大值  如果等于, 扩容阙值为integer的最大值
        if (oldCapacity == MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return;
        }
        // 如果不相等,根据新的数组长度创建数组
        Entry[] newTable = new Entry[newCapacity];
        // 移动老的数组中的数据到新的数组
        transfer(newTable, initHashSeedAsNeeded(newCapacity));
        // 把table的引用指向新的数组
        table = newTable;
        // 获取扩容的阙值
        threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
    }

    // 移到老的数组中的数据到新的数组里面
    void transfer(Entry[] newTable, boolean rehash) {
    	// 获取新的数组的长度
        int newCapacity = newTable.length;
        // 遍历老的entry数组
        for (Entry<K,V> e : table) {
        	// 判断entry不为null
            while(null != e) {
            	// 获取链表中的entry
                Entry<K,V> next = e.next;

                if (rehash) {
                	// 获取hash值
                    e.hash = null == e.key ? 0 : hash(e.key);
                }
                // 通过hash与新的数组长度,获取key在新的数组中的下标
                int i = indexFor(e.hash, newCapacity);
                // 当前entry添加到数组之前,先原来的entry存进当前entry下
                e.next = newTable[i];
                // 把当前entry赋给entry数组
                newTable[i] = e;
                // 对当前entry链表的下一个entry进行赋值
                e = next;
            }
        }
    }

1.8 首先根据 key 的值计算 hash 值,找到该元素在数组中存储的下标;

如果数组是空的,则调用 resize 进行初始化;

如果没有哈希冲突直接放在对应的数组下标里;

如果冲突了,且 key 已经存在,就覆盖掉 value;

如果冲突后,发现该节点是红黑树,就将这个节点挂在树上;

如果冲突后是链表,判断该链表是否大于 8 ,如果大于 8 并且数组容量小于 64,就进行扩容;

如果链表长度大于 8 并且数组的容量大于等于 64,则将这个结构转换为红黑树;

否则,链表插入键值对,若 key 存在,就覆盖掉 value。

Hash函数是指把一个大范围映射到一个小范围,目的往往是为了节省空间,使得数据容易保存。比较出名的有MurmurHash、MD4、MD5等等

一般用Integer、String 这种不可变类当 HashMap 当 key,而且 String 最为常用。

因为字符串是不可变的,所以在它创建的时候 hashcode 就被缓存了,不需要重新计算。这就是 HashMap 中的键往往都使用字符串的原因。
因为获取对象的时候要用到 equals() 和 hashCode() 方法,那么键对象正确的重写这两个方法是非常重要的,这些类已经很规范的重写了 hashCode() 以及 equals() 方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值