JDK源码-HashMap

一、HashMap的数据结构?为什么要采用这样的数据结构?

//默认 HashMap 集合初始容量为16(必须是 2 的倍数)
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
//集合的最大容量,如果通过带参构造指定的最大容量超过此数,默认还是使用此数
static final int MAXIMUM_CAPACITY = 1 << 30;
//默认的填充因子
static final float DEFAULT_LOAD_FACTOR = 0.75f;
//当桶(bucket)上的结点数大于这个值时会转成红黑树(JDK1.8新增)
static final int TREEIFY_THRESHOLD = 8;
//当桶(bucket)上的节点数小于这个值时会转成链表(JDK1.8新增)
static final int UNTREEIFY_THRESHOLD = 6;
/**(JDK1.8新增)
 * 当集合中的容量大于这个值时,表中的桶才能进行树形化 ,否则桶内元素太多时会扩容,
 * 而不是树形化 为了避免进行扩容、树形化选择的冲突,这个值不能小于 4 * TREEIFY_THRESHOLD
 */
static final int MIN_TREEIFY_CAPACITY = 64;
HashMap初始属性
//hash(key)获取Key的哈希值,equls返回为true,则两者的hashcode一定相等,意即相等的对象必须具有相等的哈希码。
public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}

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

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    //创建Node数组           
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    //如果table为null,扩容resize()
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    //(n - 1) & hash按位与运算快,除法运算效率低
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);//tab[i] 为null,直接将新的key-value插入到计算的索引i位置
    else {
        //否则tab[i] 不为null,表示该位置已经有值了。如果key相同,直接覆盖value值,==和equals判断
        Node<K,V> e; K k;
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;
        //如果是红黑树
        else if (p instanceof TreeNode)
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        //如果是链表
        else {
            //遍历链表
            for (int binCount = 0; ; ++binCount) {
                if ((e = p.next) == null) {
                    p.next = newNode(hash, key, value, null);
                    //当长度大于8转化为红黑树
                    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;
            }
        }
        if (e != null) { // existing mapping for key
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);
            return oldValue;
        }
    }
    ++modCount;
    //扩容
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}
put方法

 

JDK1.8前是链表+数组,JDK1.8后是链表+数组+红黑树。从put方法进去看,创建Node<key,value>[] table数组,默认长度16(2的幂次方,方便后面的与运算,hash值计算),LOAD_FACTOR负载因子默认0.75(存放Node超过8时会自动扩容两倍)。

key经过hash()运算,返回最大程度的每次hash值不同,进行右移运算,让高位参与运算,减少hash碰撞,随后通过indexFor(h,length)定位到数组下标(h & length-1),与运算和去取模运算得出结果一致,与运算执行效率高。

为什么使用红黑树?平衡二叉树?

 

posted @ 2021-09-12 16:36  不仅仅七月安生  阅读(37)  评论(0)    收藏  举报