HashMap原理

HashMap中定义了一个Node数组table用来存储元素。

 transient Node<K,V>[] table;

如果调用HashMap的无参构造器创建HashMap对象,一开始,HashMap不会实例化table数组。只有第一次调用put(K key, V value)方法时,HashMap才会table创建一个初始容量为16的Node<K,V>[]。

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

如果当插入的键值对数量大于threshold值时,HashMap为会table数组扩容成原来容量的2倍。其中的threshold=(int)(DEFAULT_LOAD_FACTOR * table.length),也就是说,当键值对数量大于装载因子与table数组长度的乘积时,HashMap才会扩容。下面是具体扩容方法。

final Node<K, V>[] resize() {
		Node<K, V>[] oldTab = table;
		int oldCap = (oldTab == null) ? 0 : oldTab.length;
		int oldThr = threshold;
		int newCap, newThr = 0;
		if (oldCap > 0) {
			if (oldCap >= MAXIMUM_CAPACITY) {
				threshold = Integer.MAX_VALUE;
				return oldTab;
			} else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY)
				newThr = oldThr << 1; // double threshold
		} else if (oldThr > 0) // initial capacity was placed in threshold
			newCap = oldThr;
		else { // zero initial threshold signifies using defaults
			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<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 { // 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是怎样通过插入的key值的hashcode确定其在table数组中的位置呢?
首先对key的hashcode值无符号右移16位与其hashcode值进行异或运算,具体计算过程如下:

 static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

然后在用上一过程得到的新hash值和table.length-1做&运算得到其在table中的位置,如果此位置上为null,也就是没有元素,则直接新建一个Node存储在该位置上。

 if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);

如果此位置上不为空分为以下几种情况

1.此位置上的节点不是TreeNode类型,并且插入元素的key值equals.(此节点的key值)为true时,用传入进来的value值覆盖原有的value值。

  if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))
       e = p;//先将原节点暂存到e中
//覆盖
  if (e != null) { // existing mapping for key
       V oldValue = e.value;
       if (!onlyIfAbsent || oldValue == null)
                e.value = value;//用新value值将原value值覆盖
                afterNodeAccess(e);
                return oldValue;

2.此位置上的节点不是TreeNode类型,并且插入元素的key值equals.(此节点的key值)为false时,那么以此结点为头结点,向下寻找子结点,如果子结点的key值equals.(key值)为true时,同样用新value值将原来结点处的value值覆盖,否则直到子结点的next值为null时,新建一个Node存储在next位置。之后判断该链表的长度是否大于等于8,如果成立,则调用treeifyBin(Node<K,V>[] tab, int hash)将此链表树化

 else {
       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);
                        break;
             }
             if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))
                   break;//执行到此处时,e所只向的节点为将要被覆盖的节点
             p = e;
       }
//覆盖
  if (e != null) { // existing mapping for key
       V oldValue = e.value;
       if (!onlyIfAbsent || oldValue == null)
                e.value = value;//用新value值将原value值覆盖
                afterNodeAccess(e);
                return oldValue;

3.如果此位置上的节点是TreeNode类型,则调用putTreeVal(HashMap<K,V> map, Node<K,V>[] tab,int h, K k, V v)方法将其插入树中。

 else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);

大家可能会好奇为什么HashMap.Node类型的数组能存储HashMap.TreeNode类型呢,其实HashMap.TreeNode是HashMap.Node的间接子类,HashMap.TreeNode继承了LinkedHashMap.Entry类,而LinkedHashMap.Entry又继承了HashMap.Node类。所以table数组中能存储HashMap.TreeNode类型。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值