HashMap源码解析

待完善

基于jdk1.8的HashMap源码

默认字段:
	static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;   //默认Node的数组长度 16
	static final int MAXIMUM_CAPACITY = 1 << 30; //数组的最大长度 2^30
	static final float DEFAULT_LOAD_FACTOR = 0.75f;  //负载因子  扩容的阀值 = 数组长度 * 负载因子
	static final int TREEIFY_THRESHOLD = 8; //链表变为红黑树的长度  即树化的长度
	static final int UNTREEIFY_THRESHOLD = 6; //红黑树变为链表的大小

map.put();源码


 public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
   }


/**
* hash:key的哈希值
* key:要存入的key
* value:要存入的value
* onlyIfAbsent:为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;
        //如果Node数组没有初始化 进行初始化 n为node数组的长度
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        //hash散列到对应的数组位置  如果数组中没有值  就新建一个Node 放入对应的位置中
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        //对应的位置已经有了元素
        else {
            Node<K,V> e; K k;
            //如果要存入的key和当前的key相等 将对应的位置节点元素赋值给e
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            //如果当前位置的元素是一个红黑树类型的  e就指向了一个红黑树节点
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                //走到这块证明是个链表  binCount:记录链表的节点个数
                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);
                        //从这块退出的时候e是null
                        break;
                    }
                    //如果e和key的值相等  就退出循环
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        //从这块退出的时候e指向的是map中已经存在的key节点
                        break;
                    //e指向的是p的后驱元素
                    p = e;
                }
            }
            //如果e不为空 说明当前的key已经存在了map当中  返回key对应的旧的value   
            //遵循相同的键值覆盖
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    //e的值设为value
                    e.value = value;
                afterNodeAccess(e);
                //返回旧的值
                return oldValue;
            }
        }
        //map在结构上面被改变的次数 如put、del方法都会使这个值加一
        ++modCount;
        //如果元素个数大于阀值的话   map进行扩容
        //疑问点: hash扩容应该是数组中的元素个数。。。。
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

resize()源码


final Node<K, V>[] resize() {
    //获取已有数组的指针
    Node<K, V>[] oldTab = table;
    //获取原来数组长度
    int oldCap = (oldTab == null) ? 0 : oldTab.length;
    //获取原有数组扩容的阀值
    int oldThr = threshold;
    int newCap, newThr = 0;
    //如果原有数组的长度大于0  即数组已经初始化
    if (oldCap > 0) {
        if (oldCap >= MAXIMUM_CAPACITY) {
            //如果原有数组长度已经超过了最大值
            threshold = Integer.MAX_VALUE;
            //直接返回  不能进行扩容了
            return oldTab;
        }
        //原有数组乘2小于数组最大容量(2的30次方)  并且大于等于默认的数组长度的大小(16)扩容的阀值乘以2
        else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                oldCap >= DEFAULT_INITIAL_CAPACITY)
            newThr = oldThr << 1; // double threshold
    } else if (oldThr > 0) // 初始容量设置为阈值(老数组的长度不大于0 并且它的阀值大于0)
        newCap = oldThr;
    else {               // 初始阈值为0表示使用默认值
        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数组、长度为上述一系列操作后确认的值(一般为老的数组长度的两倍)
    Node<K, V>[] newTab = (Node<K, V>[]) new Node[newCap];
    table = newTab;
    if (oldTab != null) {
        //遍历老的Node数组  去往新的数组中填充元素
        for (int j = 0; j < oldCap; ++j) {
            Node<K, V> e;
            if ((e = oldTab[j]) != null) {
                oldTab[j] = null;
                //如果元素没有后驱的节点
                if (e.next == null)
                    //用元素的hash值 重新定位到新的Node数组中(这块有个好处  就是元素永远都在原来的位置或者原来的位置+原来数组的长度的位置)
                    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;
                    //当前元素要么还在当前位置 要么就在当前位置+原来数组长度的位置
                    //loHead重新索引后再当前位置整个链表的头节点、loTail为尾节点
                    //hiHead重新索引后在当前位置+原来数组长度位置的链表头结点,hiTail为尾节点
                    do {
                        //记下当前节点的下一节点
                        next = e.next;
                        //这块的链表重排使用的是尾插法  hiTail.next = e;      hiTail = e;
                        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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值