HashMap源码

HashMap源码:

/**
 * Hash table based implementation of the <tt>Map</tt> interface.  This
 * implementation provides all of the optional map operations, and permits
 * <tt>null</tt> values and the <tt>null</tt> key.  (The <tt>HashMap</tt>
 * class is roughly equivalent to <tt>Hashtable</tt>, except that it is
 * unsynchronized and permits nulls.)  This class makes no guarantees as to
 * the order of the map; in particular, it does not guarantee that the order
 * will remain constant over time.
 */

大致意思是Hash Table是基于Map接口实现的,并且HashMap容许null key和null value,HashMap和HashTable很类似,除了HashMap不是线程安全的和允许key和value为空外,并且他不能保证随着时间的推移,顺序保持不变;

可以看到HashMap和HashTable分别插入null的情况;

紧接着是下一段注释:

/**An instance of <tt>HashMap</tt> has two parameters that affect its
 * performance: <i>initial capacity</i> and <i>load factor</i>.  The
 * <i>capacity</i> is the number of buckets in the hash table, and the initial
 * capacity is simply the capacity at the time the hash table is created.  The
 * <i>load factor</i> is a measure of how full the hash table is allowed to
 * get before its capacity is automatically increased.  When the number of
 * entries in the hash table exceeds the product of the load factor and the
 * current capacity, the hash table is <i>rehashed</i> (that is, internal data
 * structures are rebuilt) so that the hash table has approximately twice the
 * number of buckets.
 */

大致意思是HashMap有两个影响其性能的参数:初始容量和负载因子,其中初始容量的意思是HashMap中初始化的时候的容量大小,负载因子的表示一个散列表的空间的使用程度,当哈希表中的数量超过load factor和当前容量的乘积时,哈希表将被rehash(即重新构建内部数据结构);

所以负载因子越大则散列表的装填程度越高,也就是能容纳更多的元素,元素多了,链表大了,所以此时索引效率就会降低。

反之,负载因子越小则链表中的数据量就越稀疏,此时会对空间造成烂费,但是此时索引效率高。

/**
  * Constructs an empty <tt>HashMap</tt> with the default initial capacity
  * (16) and the default load factor (0.75).
  */
public HashMap() {
    this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}

通过源码可以看到初始容量大小为16,负载因子为0.75,因此当HashMap的大小为12的时候,会出发rehash;

 

HashMap中的put方法

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数组,p表示tab上的其中一个Node节点,n表示tab的长度,i表示tab的下标。
    if ((tab = table) == null || (n = tab.length) == 0) //判断当前的tab中是否为空,或者数组的长度是否为空
        n = (tab = resize()).length;					//如果为空,则调用resize方法初始化数组,默认长度为16
    if ((p = tab[i = (n - 1) & hash]) == null)			//(n - 1) & hash 计算出的值作为tab的下标i,并另p为tab[i],也就是该链表第一个节点的位置。并判断p是否为null。
        tab[i] = newNode(hash, key, value, null);		//如果p为null,这表示tab[i]上没有任何元素,则根据key和value创建一个
    else {												//产生hash冲突
        Node<K,V> e; K k;
        if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k)))) //如果参数中的hash值和原有的hash值相等,并且参数中的key和原有的key相等
            e = p;										//则将原有的值付给节点e上
        else if (p instanceof TreeNode)					//如果存在的值,它的类型是一棵树
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); //则调用putTreeVal方法
        else {											//如果他既不是一棵树,key不相同,则它是链表
            for (int binCount = 0; ; ++binCount) {		//遍历链表
                if ((e = p.next) == null) {				//如果next为空,则表明后面没有元素了,只需把新的节点赋给p.next
                    p.next = newNode(hash, key, value, null);	//则创建新的节点赋值给已有的next属性
                    if (binCount >= TREEIFY_THRESHOLD - 1) 		//插入成功后,判断是否需要转为红黑树,这里减一是因为上面bincount还没有自增,因此这里需要减一进行判断
                        treeifyBin(tab, hash);  				//如果链表长度达到了8,数组长度小于64,那么就resize调整大小,如果大于64,则转化为红黑树
                    break;
                }
                if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) //如果hash值和next的hash值相同并且且key也相同,则跳出循环,因为这时候不需要插入,只需替换即可
                    break;
                p = e;											//将next 值赋给 p,为下次循环做铺垫
            }
		}
		//如果e部位null,则表示该元素存在
		if (e != null) { // existing mapping for key			//源代码的注释解释很清楚,就是对已存在的可以做处理
			V oldValue = e.value;								//取出该元素的值
			if (!onlyIfAbsent || oldValue == null)				//如果onlyIfAbsent这个参数是true,就不改变已有的值,如果是false则改变,默认该参数为false
				e.value = value;								//将已有的值替换掉
			afterNodeAccess(e);									//这个函数不作任何处理
			return oldValue;									//这里竟然返回的是oldvalue,因为使用put方法的时候很少关注他的返回值,甚至在不看源码的时候都不知道put方法竟然有返回值
		}
	}
	//如果e为空
    ++modCount;													//为了提供给后面的foreach和迭代器所使用
    if (++size > threshold)										//如果数组长度大于了阀值
        resize();												//进行扩容
    afterNodeInsertion(evict);									//这个函数不作任何处理
    return null;												//返回null
}

限于篇幅原因,在put方法里面调用到的其他方法就不细说了,有兴趣的同学也可以自己去研究一下。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值