HashMap源码详解下篇

143 阅读3分钟

上篇讲完了基本属性和三个构造方法,添加数据的put方法以及获取数据的get方法。接下来来重点说一下hashmap中的扩容方法。当当前容量>数组大小* 加载因子则会进行一次扩容操作,具体的过程来看一下


final Node<K,V>[] resize() {
		//用于存储扩容之前的散列表也就是数组
        Node<K,V>[] oldTab = table;
        //记录扩容之前的数组大小
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
        //表示扩容之前的扩容阈值
        int oldThr = threshold;
        //记录扩容后的新容量,newThr发生下次扩容的扩容阈值
        int newCap, newThr = 0;
        //如果满足条件说明散列表已经初始化过了,这是一次正常的扩容过程
        if (oldCap > 0) {
        	//如果容量已经达到了最大容量,则不进行扩容,返回最大容量
            if (oldCap >= MAXIMUM_CAPACITY) {
                threshold = Integer.MAX_VALUE;
                return oldTab;
            }
            //判断新容量大小=旧容量大小*2是否小于最大容量并且旧容量大小大于默认容量大小16
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                //新的扩容阈值等于旧的扩容阈值*2
                newThr = oldThr << 1; // double threshold
        }
        //来到这里说明旧容量大小=0
        //通过可以传入初始容量大小=0的构造方法初始化的
        else if (oldThr > 0) // initial capacity was placed in threshold
        	//新容量大小会等于扩容之前的扩容阈值
            newCap = oldThr;
            
        //oldcap=0,oldThr=0的情况    
        else {              
        	//新容量=默认初始容量=16
            newCap = DEFAULT_INITIAL_CAPACITY;
            //新的扩容阈值=(int)0.75f*16=12
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
        }
        //新的扩容阈值=0的情况
        if (newThr == 0) {
        	//如果新容量大小<最大容量大小并且下一次的扩容阈值小于最大容量则返回(int)ft
            float ft = (float)newCap * loadFactor;
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                      (int)ft : Integer.MAX_VALUE);
        }
        
        threshold = newThr;
        @SuppressWarnings({"rawtypes","unchecked"})
        //创建一个新的更大数组newTab用于存储旧数组的数据
        Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
        table = newTab;
        //本次扩容前,旧数组不为空
        if (oldTab != null) {
            for (int j = 0; j < oldCap; ++j) {
            	//当前节点
                Node<K,V> e;
                //当前桶位有数据
                if ((e = oldTab[j]) != null) {
                	//置空方便垃圾回收
                    oldTab[j] = null;
                    //因为下一个为空,说明是头节点,通过计算放到新的数组中
                    if (e.next == null)
                        newTab[e.hash & (newCap - 1)] = e;
                    //判断是否要树化
                    else if (e instanceof TreeNode)
                        ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                    //链表的情况
                    else { 
                    	//低位链表,存放扩容之后的下标的位置,与扩容之前的一致
                        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) {
                                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);
                        if (loTail != null) {
                            loTail.next = null;
                            newTab[j] = loHead;
                        }
                        if (hiTail != null) {
                            hiTail.next = null;
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }
        }
        return newTab;
    }

补充一下删除节点方法

//传入key
public V remove(Object key) {
        Node<K,V> e;
        return (e = removeNode(hash(key), key, null, false, true)) == null ?
            null : e.value;
    }

//传入key-value
public boolean remove(Object key, Object value) {
        return removeNode(hash(key), key, value, true, true) != null;
    }
    
final Node<K,V> removeNode(int hash, Object key, Object value,
                               boolean matchValue, boolean movable) {
         //tab:引用当前hashmap中的散列表
         //p:当前node元素
         //n:散列表数组长度
         //index:表示寻址结果
        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:查找到的结果
            //e:当前Node的下一个元素
            Node<K,V> node = null, e; K k; V v;
            
            //情况1:当前桶位中的元素即为要删除的元素
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                node = p;
            else if ((e = p.next) != null) {
                //说明当前桶位,要么是链表,要么是红黑树
                
                //判断当前桶位是否升级为红黑树
                if (p instanceof TreeNode)
                    
                    node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
                else {
                    //链表的情况
                    do {
                        if (e.hash == hash &&
                            ((k = e.key) == key ||
                             (key != null && key.equals(k)))) {
                            node = e;
                            break;
                        }
                        p = e;
                    } while ((e = e.next) != null);
                }
            }
            
            //判断node不为空的话,说明按照key查找到需要删除的数据了
            if (node != null && (!matchValue || (v = node.value) == value ||
                                 (value != null && value.equals(v)))) {
                //情况1:node是树节点,说明需要进行树节点移除操作
                if (node instanceof TreeNode)
                    ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
         //情况2:桶位元素即为查找结果,则将该元素的下一个元素放置桶位中
                else if (node == p)
                    tab[index] = node.next;
                //情况3:将当前元素p的下一个元素设置成要删除元素的下一个元素
                else
                    p.next = node.next;
                ++modCount;
                --size;
                afterNodeRemoval(node);
                return node;
            }
        }
        return null;
    }