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
    评论
HashMap是Java中的一个常用的集合类,用于存储键值对的数据结构。它的底层数据结构是数组、链表和红黑树。HashMap可以存储null的键和值。下面是对HashMap源码的详细分析。 1. HashMap的特点:HashMap是无序的,不保证元素的顺序。它允许使用null作为键和值。HashMap不是线程安全的,如果在多线程环境下使用,需要进行额外的同步。 2. HashMap的构造方法: - HashMap():创建一个空的HashMap实例,默认初始容量为16,负载因子为0.75。 - HashMap(int initialCapacity):创建一个指定初始容量的HashMap实例。 - HashMap(int initialCapacity, float loadFactor):创建一个指定初始容量和负载因子的HashMap实例。 - HashMap(Map<? extends K, ? extends V> m):创建一个包含指定Map中所有键值对的HashMap实例。 3. HashMap的成员方法: - put(K key, V value):向HashMap中添加键值对。 - hash方法:计算键的哈希值。 - 扩容方法resize():当HashMap的大小达到阈值时,会自动扩容。 - 删除方法remove():根据键删除对应的键值对。 - 查找元素方法get():根据键获取对应的值。 4. HashMap的常见问题: - HashMap的容量为什么是2的幂次方?这是为了提高哈希函数的散列性能和数组的索引计算效率。 - 为什么负载因子默认是0.75?这是为了在保持较高的查找效率的同时,尽可能减少哈希冲突和扩容次数。 综上所述,HashMap是一种底层使用数组、链表和红黑树的存储方式的键值对集合类,并提供了一系列的方法来操作和查询数据。它的源码实现细节涉及到数组的扩容、哈希函数的计算、链表和红黑树的操作等。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [最详细的Hashmap源码解析](https://blog.csdn.net/qq_45830276/article/details/126768408)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [HashMap源码解析](https://blog.csdn.net/weixin_46129192/article/details/123287837)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值