HashMap1.8深度解析

什么是HashMap?

百度百科:基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。(除了非同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同。)

HashMap各个参数的作用

//默认初始化容量 1往右移4
//0000 0001-->0001 0000; 10000
转二进制=16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
//
最大容量 1往右移30 =230次方
static final int MAXIMUM_CAPACITY = 1 << 30;
//默认加载因子0.75,用途是当存储容量到达75%时进行扩容
static final float DEFAULT_LOAD_FACTOR = 0.75f;
//树阈值 当该阈值>=8时将会由链表转红黑树
static final int TREEIFY_THRESHOLD = 8;
//非树阈值 当该阈值<=6时,由红黑树转链表
static final int UNTREEIFY_THRESHOLD = 6;
//最小树容量,表示当数组容量至少存储了64个以后才能转红黑树
static final int MIN_TREEIFY_CAPACITY = 64;

//当前数组存储的数量

transient int size;

//修改次数,目的是为了防止在get的同时进行put操作

transient int modCount;

//扩容阈值 resize (capacity * load factor)

int threshold;

//加载因子

float loadFactor;

//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;

    }



    public final K getKey()        { return key; }

    public final V getValue()      { return value; }

    public final String toString() { return key + "=" + value; }

    public final int hashCode() {

        return Objects.hashCode(key) ^ Objects.hashCode(value);

    }

    public final V setValue(V newValue) {

        V oldValue = value;

        value = newValue;

        return oldValue;

    }



    public final boolean equals(Object o) {

        if (o == this)

            return true;

        if (o instanceof Map.Entry) {

            Map.Entry<?,?> e = (Map.Entry<?,?>)o;

            if (Objects.equals(key, e.getKey()) &&

                    Objects.equals(value, e.getValue()))

                return true;

        }

        return false;

    }

}

HashMap的hash方法

//根据keyhashCode值计算HashMaphash

static final int hash(Object key) {

    int h;

    //如果当前key==null,那么hash值为0

    //h ^ (h >>> 16)

    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);

}

HashMap获取数组Node节点

//获取链表

final Node<K,V> getNode(int hash, Object key) {

    Node<K,V>[] tab; Node<K,V> first, e; int n; K k;

    //table不为空且table长度大于0且获取的链表也不为空

    if ((tab = table) != null && (n = tab.length) > 0 &&

            (first = tab[(n - 1) & hash]) != null) {

        //判断当前的链表hash+equals是否匹配,匹配返回当前Node节点

        if (first.hash == hash && // always check first node

                ((k = first.key) == key || (key != null && key.equals(k))))

            return first;

        //若是不匹配,则轮询查找

        if ((e = first.next) != null) {

            //若当前node节点为红黑树,根据红黑树的查找方式获取TreeNode

            if (first instanceof TreeNode)

                return ((TreeNode<K,V>)first).getTreeNode(hash, key);

            //否则轮询查找获取该Node节点

            do {

                if (e.hash == hash &&

                        ((k = e.key) == key || (key != null && key.equals(k))))

                    return e;

            } while ((e = e.next) != null);

        }

    }

    return null;

}

HashMap初始化

//HashMap有参构造函数

public MyHashMap(int initialCapacity, float loadFactor) {

    //若初始化容量<0 报错

    if (initialCapacity < 0)

        throw new IllegalArgumentException("Illegal initial capacity: " +

                initialCapacity);

    //若初始化容量>设定的最大容量,初始化容量=最大容量

    if (initialCapacity > MAXIMUM_CAPACITY)

        initialCapacity = MAXIMUM_CAPACITY;

    //若加载因子<=0,或加载因子不为数值,报错

    if (loadFactor <= 0 || Float.isNaN(loadFactor))

        throw new IllegalArgumentException("Illegal load factor: " +

                loadFactor);

    this.loadFactor = loadFactor;

    this.threshold = tableSizeFor(initialCapacity);

}

HashMap的put

public V put(K key, V value) {

    //新增元素

    return putVal(hash(key), key, value, false, true);

}



final V putVal(int hash, K key, V value, boolean onlyIfAbsent,

               boolean evict) {

    Node<K,V>[] tab; Node<K,V> p; int n, i;

    //若数组为空或者数组长度为0,进行扩容

    if ((tab = table) == null || (n = tab.length) == 0)

        //扩容后赋值当前table长度给n

        n = (tab = resize()).length;

    //若是利用(n-1) & hash获取tablei,获取table[i]判断node是否为空,为空就创建一个新node给该table[i]

    if ((p = tab[i = (n - 1) & hash]) == null)

        tab[i] = newNode(hash, key, value, null);

    else {

        //否则就进行追加

        Node<K,V> e; K k;

        //判断当前node节点的key.hash+equals是否匹配

        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循环判断node节点next是否为空,为空则追加。此法为尾插法

            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

                        //若是当前节点数已经大于等于7-1是因为去除了第一个。实际上是大于等于8

                        //转红黑树

                        treeifyBin(tab, hash);

                    break;

                }

                //若是能找到该key匹配的node节点,也停止查找,稍后将赋值新value

                if (e.hash == hash &&

                        ((k = e.key) == key || (key != null && key.equals(k))))

                    break;

                p = e;

            }

        }

        //e为空,说明p.next是新node,若不为空,说明是已经存在的node,现在要做的就是赋值新value

        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;

}
final void treeifyBin(Node<K,V>[] tab, int hash) {

    int n, index; Node<K,V> e;

    //若当前table数量<64,则扩容但不转红黑树

    //即转红黑树的条件为table>=64,且该链表>=8才符合要求

    if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)

        resize();

    else if ((e = tab[index = (n - 1) & hash]) != null) {

        //若该node节点不为空,则走该分支。其实已经确认过。

        //转红黑树

        TreeNode<K,V> hd = null, tl = null;

        do {

            TreeNode<K,V> p = replacementTreeNode(e, null);

            if (tl == null)

                hd = p;

            else {

                p.prev = tl;

                tl.next = p;

            }

            tl = p;

        } while ((e = e.next) != null);

        if ((tab[index] = hd) != null)

            hd.treeify(tab);

    }

}

HashMap的get

//get方法

public V get(Object key) {

    Node<K,V> e;

    //根据当前key计算出hash值,并利用getNode方法比较hash+equals方法获取value

    return (e = getNode(hash(key), key)) == null ? null : e.value;

}

HashMap扩容

final Node<K,V>[] resize() {

    Node<K,V>[] oldTab = table;

    //oldCap=table长度,oldThr = 阈值

    int oldCap = (oldTab == null) ? 0 : oldTab.length;

    int oldThr = threshold;

    int newCap, newThr = 0;

    if (oldCap > 0) {

        //若旧table长度已经到达最大容量

        if (oldCap >= MAXIMUM_CAPACITY) {

            //阈值为integer最大值

            threshold = Integer.MAX_VALUE;

            return oldTab;

        }

        //oldCap左移1位,也就是2

        else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&

                oldCap >= DEFAULT_INITIAL_CAPACITY)

            //新阈值=旧阈值的2

            newThr = oldThr << 1; // double threshold

    }

    else if (oldThr > 0) // initial capacity was placed in threshold

        //table长度为0,阈值大于0,把阈值赋值给新table长度

        newCap = oldThr;

    else {               // zero initial threshold signifies using defaults

        //table长度为默认长度 16

        newCap = DEFAULT_INITIAL_CAPACITY;

        //新阈值=默认加载因子0.75 * 默认长度16

        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;

    //扩容时将旧table中的数据移动到新table

    if (oldTab != null) {

        for (int j = 0; j < oldCap; ++j) {

            Node<K,V> e;

            //oldTable对应的链表赋值给e,并将oldTable对应的index清空,为了垃圾回收

            if ((e = oldTab[j]) != null) {

                oldTab[j] = null;

                //e.next为空,说明当前链表只有一个node

                if (e.next == null)

                    //e赋值给新table对应的index

                    newTab[e.hash & (newCap - 1)] = e;

                else if (e instanceof TreeNode)

                    //判断e是否为红黑树,若是即按照红黑树的情况来增加元素

                    ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);

                else { // preserve order

                    //否则使用高低node节点来拆分链表

                    //低头链表node

                    Node<K,V> loHead = null, loTail = null;

                    //高头链表node

                    Node<K,V> hiHead = null, hiTail = null;

                    Node<K,V> next;

                    do {

                        next = e.next;

                        //利用hash & table长度判断是否==0

                        //注:我们知道hash & (length -1)是为了均匀存放key

                        // 那么 hash & length就只有两个结果,要么0,要么当前table长度

                        //举个例子: 1: 0000 0001   1&16=0

                        //          2: 0000 0010   2&16=0

                        //          3: 0000 0011   3&16=0

                        //         17: 0001 0001   17&16=16

                        //         16: 0001 0000

                        if ((e.hash & oldCap) == 0) {

                            if (loTail == null)

                                loHead = e;

                            else

                                loTail.next = e;

                            loTail = e;

                        }

                        else {

                            if (hiTail == null)

                                hiHead = e;

                            else

                                hiTail.next = e;

                            hiTail = e;

                        }

                    } while ((e = next) != null);

                    //高低链表二者用的都是尾插法

                    //低链表为新table[index]原链表

                    if (loTail != null) {

                        loTail.next = null;

                        newTab[j] = loHead;

                    }

                    //高链表为新table[index+ oldTable长度]新链表

                    //为何是j+oldCap,因为每次扩容都是2倍,这样的话就直接分摊了2个链表,又避免了1.7死循环的bug

                    if (hiTail != null) {

                        hiTail.next = null;

                        newTab[j + oldCap] = hiHead;

                    }

                }

            }

        }

    }

    return newTab;

}

删除HashMap元素

public V remove(Object key) {

    Node<K,V> e;

    //删除

    return (e = removeNode(hash(key), key, null, false, true)) == null ?

            null : e.value;

}

final Node<K,V> removeNode(int hash, Object key, Object value,

                                             boolean matchValue, boolean movable) {

    Node<K,V>[] tab; Node<K,V> p; int n, index;

    if ((tab = table) != null && (n = tab.length) > 0 &&

            (p = tab[index = (n - 1) & hash]) != null) {

        Node<K,V> node = null, e; K k; V v;

        //根据hash查找链表

        if (p.hash == hash &&

                ((k = p.key) == key || (key != null && key.equals(k))))

            //根据hash+equals获取node

            node = p;

        else if ((e = p.next) != null) {

            if (p instanceof TreeNode)

                //若为红黑树,按照红黑树的规则获取node

                node = ((TreeNode<K,V>)p).getTreeNode(hash, key);

            else {

                //该节点找不到,则轮询next

                do {

                    if (e.hash == hash &&

                            ((k = e.key) == key ||

                                    (key != null && key.equals(k)))) {

                        node = e;

                        break;

                    }

                    p = e;

                } while ((e = e.next) != null);

            }

        }

        //找到以后按照规则删除

        if (node != null && (!matchValue || (v = node.value) == value ||

                (value != null && value.equals(v)))) {

            if (node instanceof TreeNode)

                //若该节点为树,就按照树节点删除

                ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);

            else if (node == p)

                //若当前node是头节点,那么头结点的nexttable[index]

                tab[index] = node.next;

            else

                //此时pnode的上一个节点,p.next=node.next。正好去除该node

                p.next = node.next;

            //++修改次数

            ++modCount;

            //--数组容量

            --size;

            afterNodeRemoval(node);

            return node;

        }

    }

    return null;

}

public void clear() {

    //清除,每个table对应的链表置为空

    Node<K,V>[] tab;

    modCount++;

    if ((tab = table) != null && size > 0) {

        size = 0;

        for (int i = 0; i < tab.length; ++i)

            tab[i] = null;

    }

}

HashMap1.7与1.8的区别

  1. 初始值容量改成1 << 4,扩容改成newCap = oldCap << 1,目的在于使用二进制计算更快
  2. 解决了扩容死锁的问题,并且利用高低链表的方式,快速拆分链表并存入新的链表。而且增加了红黑树,解决链表过程时间复杂度高的情况。1.7使用的是头插法,而1.8使用的是尾插法
  3. 在写法上,1.81.7的代码水平提高了档次。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值