HashMap的部分源码

本文详细解析了JavaHashMap的内部数据结构,包括哈希表的数组设计、默认容量、负载因子,以及元素添加时遇到的不同情况处理,如数组位置、键冲突和红黑树的使用。
摘要由CSDN通过智能技术生成
Node<K,V> table //哈希表结构中数组的名字
DEFAULT_INITIAL_CAPACITY = 1 << 4;//数组默认长度16
DEFAULT_LOAD_FACTOR = 0.75f;//默认加载因子?

//HashMap里边每一个对象包含以下内容:
//1.1 链表中的键值对对象,包含:
	final int hash;//键的哈希值
	final K key;//键
    V value;//值
    Node<K,V> next;//下一个节点地址值
//1.2 红黑树中的键值对对象
	int hash;//哈希值
	final K key;//键
	V val;//值
	TreeNode<K,V> parent;  //父节点的地址值
    TreeNode<K,V> left;//左子节点地址值
	TreeNode<K,V> right;//右子节点地址值
    TreeNode<K,V> prev;//needed to unlink next upon deletion(目前不懂)
    boolean red;//节点颜色

//添加元素
HashMap<String,Integer> hm = new HashMap<>();
hm.put("aaa",111);
hm.put("bbb",222);
hm.put("ccc",333);

//添加元素至少要考虑三种情况:
1.数组位置为null;
2.数组位置不为null,键重复,元素覆盖
3.数组位置不为null,键不重复,挂在下面形成链表或红黑树





//参数一:键
//参数二:值
//返回值:被覆盖元素的值,如果没有覆盖,返回null
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);
    }

//参数一:键的哈希值
//参数二:键
//参数三:值
//参数四:如果键重复了是否保留
//		 true:表示老元素值的保留,不会覆盖
//		 false:表示老元素的值不保留,会进行覆盖
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {
 //定义一个局部变量,用来记录哈希表中数组的地址值(防止每次在堆中寻找全局变量table中的地址值,提高效率
        Node<K,V>[] tab; 
    	
    	//第三方的临时变量
    	Node<K,V> p; 
    	
    	//表示当前数组的长度
    	int n;
    	//表示索引i
    	int i;
    
    	//tab = table:把哈希表中数组的地址值赋给局部变量tab
    	//n = tab.length:把哈希表数组地址付给n
    	//resize():1.如果是第一次添加数组,底层会创建一个默认长度为16,加载因子为0.75的数组
    	//2.如果不是第一次添加数组,会判断是否达到扩容的条件
    	//	如果未达到,不做任何处理
    	//	如果达到,底层会把数组扩容为原来的两倍,并把数组全部转移到新的哈希表中
    	//n = ().length;把当前长度赋给n
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
    
    //i = (n-1) & hash 拿着数组长度计算出当前键值对对象,在数组中应该存入的位置
    //p = tab[i] //获取数组中对应元素的数据
    //newNode(),创建键值对对象,直接赋给tab[i]
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; 
            K k;
            //p.hash == hash
            //等号左边:p.hash数组中键值对的哈希值
            //等号右边:当前要添加键值对的哈希值
          	//键不一样,返回false
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)
                //p instanceof TreeNode: 判断数组中获取出来的键值对是不是红黑树中的节点
                //如果是,则调用putTreeVal,把当前节点按照红黑规则添加到树中
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                //如果不是红黑树中节点
                //表示此时挂的是链表
                for (int binCount = 0; ; ++binCount) {
                    //e = p.next
                    if ((e = p.next) == null) {
                        //此时会创建一个新的节点,并把新节点地址赋给p.next(形成链表)
                        p.next = newNode(hash, key, value, null);
                        //判断链表长度是否超过8,如果超过会调用treeifyBin()方法
                        //treeifyBin方法的底层还会继续判断,数组的长度是否大于等于64
                        //如果同时满足这两个条件,就会把它转化为红黑树
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    //要添加的元素的哈希值与数组中一样
                    //会调用key.equals()比较内部的属性值是否相同
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    //
                    p = e;
                }
            }
            //如果e==null,表示当前不需要覆盖任何元素
            //如果e不为null,表示当前键是一样的,值会被替换
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                //onlyIfAbsent为false
                if (!onlyIfAbsent || oldValue == null)
                    //等号右边:当前键要替换成的值
                    //等号左边:e键的原来的值
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
    //threshold:记录的就是数组的长度*0.75,哈希表的扩容时机,16*0.75 = 12
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
    //表示当前没有覆盖任何元素,返回null
        return null;
    }

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值