HashMap源码分析(一)

之前有写到ArrayList的源码分析,欢迎大家点开我的头像查看

对于HashMap这个类大家一定不陌生,想必多多少少用过或者了解过,今天我来和大家谈谈HashMap的源码,JDK为1.8

继承AbstractMap类,实现map接口等等

当你不设置它的容量时默认的容量大小  为16

这个属性代表着它的最大容量,大小可以理解为2的30次方

负载因子,如果构造方法没有指定负载因子的话默认0.75,也就是当它容量达到百分之75的时候扩容

这个属性代表着如果它的链表结构长度达到了8的时候链表的数据结构将会变成红黑树,提高效率

如果链表结构长度小于6的时候又会从红黑树变成链表,因为当你长度就这么长的时候链表性能反而更好

容器可以树化的最小表容量,可以理解为当它变成红黑树的最小表容量

在第一次使用的时候初始化

保存缓存的set映射 遍历map的时候需要

长度,不多说

代表它被修改的次数

常用的属性就介绍到这里,接下来我们来详细看看HashMap是怎么样put添加数据的

put方法直接是返回putVal,那我们详细看看这个方法

注释写在代码中

/**
	 *put方法的内部
     * Implements Map.put and related methods
     * @param hash hash for key
     * @param key the key
     * @param value the value to put
     * @param onlyIfAbsent if true, don't change existing value
     * @param evict if false, the table is in creation mode.
     * @return previous value, or null if none
     */
	final V putVal(int hash, K key, V value, boolean onlyIfAbsent  false, boolean evict  true) {
   
        Node<K,V>[] tab; Node<K,V> p; int n, i;
		//如果是空,也就代表是第一次put  ,
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;	//生成初始化的长度
        if ((p = tab[i = (n - 1) & hash]) == null)	//如果值==null,则证明这个数据为null
            tab[i] = newNode(hash, key, value, null);	//hash为key通过hash方法的到		将tab的第0个值设置为null
        else {	//反之则不为空时	
            Node<K,V> e; K k;	//创建一个node<>和 一个key类型的对象 k
            if (p.hash == hash &&					//如果p的hash和key的hash一致并且p的key==k或者参数key不为空并且key equals k 则将p赋给e
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)		//如果p是TreeNode类型的则进入 putTreeVal方法
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {		//如果p.next为空,则新建一个node
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st	//如果循环次数>=变成红黑树的要求数
                            treeifyBin(tab, hash);		//调整表的大小
                        break;
                    }
                    if (e.hash == hash &&		//相同hash和key则停止遍历
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;	//将e赋值给p
                }
            }
            if (e != null) { //如果e!=null,则表示map中的键有和put的键重复的,将会返回旧数据的value
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;		//更改次数+1
        if (++size > threshold)	//长度+1,如果长度大于需要扩容的大小时	resize初始化或加倍表格大小
            resize();
        afterNodeInsertion(evict);	//节点插入后的一个回调方法
        return null;
    }
	}

其中这个方法又调用了resize()方法

//初始化或加倍表格大小。如果为null,则分配符合字段阈值中保存的初始容量目标。 
	//否则,因为我们正在使用2次幂扩展,所以每个bin中的元素必须保持在相同的索引处,
	//或者在新表中以2的偏移量移动。
	
    final Node<K,V>[] resize() {
        Node<K,V>[] oldTab = table;//table为map初始化的node
        int oldCap = (oldTab == null) ? 0 : oldTab.length;	//如果为空则返回0,否则返回oldTab的长度
     int oldThr = threshold;//threshold表示到达该数map将会扩容  例如刚开始新建为16*0.75
        int newCap, newThr = 0;//newThr为要达到扩容的数	newCap为map的大小容量
        if (oldCap > 0) {	//如果map初始化的node大于0
            if (oldCap >= MAXIMUM_CAPACITY) {	//如果大于或者等于最大允许容量  1<<30
                threshold = Integer.MAX_VALUE;   //threshold 等于int的最大数值
                return oldTab;				//将map初始化的node返回
            }
    else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&    //如果 newCap==0或者大小正常  再将oldCap*2赋给newCap,判断这个newCap是否小于1<<30并且大于默认长度16
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                newThr = oldThr << 1; // double threshold	 //newThr则为下一个调整扩容的2倍
        }
        else if (oldThr > 0) // initial capacity was placed in threshold	//如果到达扩容数大于0,newCap得到到达扩容数的值
            newCap = oldThr;		
        else {               //零初始阈值表示使用默认值
            newCap = DEFAULT_INITIAL_CAPACITY;	//newCap为默认map大小16
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);	//newThr为默认将要到达则需要扩容的数   
        }
        if (newThr == 0) {		//如果将要扩容数newThr为0,
            float ft = (float)newCap * loadFactor;	//ft为map新容量*哈希表的加载因子  默认是0.75,但可以new对象时可以手动设置
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                      (int)ft : Integer.MAX_VALUE);		//当map新容量小于1<<30时并且ft小于1<<30时
														返回ft不然为int最大值 newThr为将要达到扩容时数值 (第一次为12)
        }
        threshold = newThr;		//将newThr赋值给达到将要扩容的数
        @SuppressWarnings({"rawtypes","unchecked"})
            Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];	//new一个容量为新大小的tab
        table = newTab;		//将新大小的tab赋值给	一个抑制的table
        if (oldTab != null) {		//如果旧tab不是空
            for (int j = 0; j < oldCap; ++j) {	//遍历旧tab
                Node<K,V> e;
                if ((e = oldTab[j]) != null) {	//当前node不为空
                    oldTab[j] = null;			//赋值为空
                    if (e.next == null)			//如果下一个为空
                        newTab[e.hash & (newCap - 1)] = e;	//&代表如果前面的为false后面的将不会执行
                    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) {
                                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;
    }

HashMap主要是通过这两个方法进行put操作

猜你喜欢

转载自blog.csdn.net/qq_41594146/article/details/84845764