HashMap中put方法底层代码解析

HashMap源码逻辑

基本属性

Node<K,V>[] table //哈希表结构中数组的名字

DEFAULT_INITIAL_CAPACITY //数组默认长度16

DEFAULT_LOAD_FACTOR //默认加载因子0.75

HashMap里面每一个对象包含以下内容:

链表中的键值对对象

int hash; //键的哈希值
final K key; //键
V value; //值
Node<K,V> next;//下一个节点的地址值

红黑树中的键值对对象

int hash; //键的哈希值
final K key; //键
V value; //值
TreeNode<K,V> parent; //父节点的地址值
TreeNode<K,V> left; //左子节点的地址值
TreeNode<K,V> right; //右子节点的地址值
Boolean red; //节点的颜色

在添加元素时put方法底层调用的是putVal方法,该方法需要五个参数

哈希值 hash(key)

键 key由外界传入

值 value由外界传入

若键重复是否将老元素的值覆盖 onlyIfAbsent默认为false由Java设置

evict表示被覆盖的值是否丢弃(具体意义不明,从变量名的意义理解为该意)

在putVal中由局部变量node数组tab用于记录哈希表中数组的地址值,该变量用于节约内存,将全局变量tab的值赋给局部变量tab

局部变量Node类型P用来记录键值对对象的地址值

局部变量int类型n表示当前数组的长度

局部变量i表示索引

putVal中第一步将哈希表中数组tab的地址值赋值给局部变量tab,以下tab称为数组

随后进行判断数组tab是否为空如果为空那么Java底层会调用resize方法创建一个默认长度为 16,加载因子为0.75的数组,如果不为空会判断是否满足扩容条件(数组长度*加载因子),如果满足会将数组扩容为原先的两倍并把数据全部转移到新的哈希表中若不满足则在该判断中不进行任何操作,然后将新数组的地址值赋值给tab

判断过后会拿着数组的长度和键的哈希值进行计算,计算出当前键值对对象在数组中应存入的位置并赋值给i,然后获取数组中i索引位置的数据并赋值给p

随后进行判断p是否为空,如果为空那么底层会创建一个键值对对象将键的hash值、键、值、下一个节点的地址值

如果不为空则说明该索引已经有数据,需要再判断数组中元素的键和新传入元素的键的hash值是否相同,若键的hash值相同且数组中元素的键的值和新传入元素的键的值也相同,或者新传入元素的值不为空且新传入的键和数组中元素的键相等,那么将数组中元素的地址值赋值给Node类型变量e

如果判断为false再一次判断p(数组中的元素)是否是红黑树中的树节点,若是则调用方法putTreeVal方法把当前节点按照红黑树的规则添加到树中,putTreeVal方法原理和putVal方法大同小异但多了按照红黑树方法添加的判断

若前面两次判断都为false那么Java底层将按照数组中挂载链表的情况处理

建立一个for循环,在循环中判断当前数组中元素是否有下一个节点并将地址值赋值给e,如果为空说明该元素是链表中最后一个元素,那么会创建一个新节点并将新节点的地址值赋值给上一个节点中的next变量形成链表然后判断当前链表长度超过8,如果超过8会调用方法treeifBin,该方法底层还会判断数组长度是否大于64,如果满足两个条件就会将链表转成红黑树随后结束循环,如果不为空则判断下一个节点的哈希值是否等于新传入元素的哈希值且下一个节点的键是否等于新传入元素的键,或者新传入节点的键不为空且两个节点的键相同(该判断用于判断新传入的元素是否已经在数组中存在,第一次判断两个键是否相等后再一次判断是否相等是因为当键是字符串时第一次的判断比较的是地址值而相同的内容创建的字符串地址值是不同的,而第二次比较是调用的equals方法,避免了键重复的情况),若以上条件满足则跳出循环,若不满足则将下一个节点的地址值赋值给p进行下一次判断直到最后一个元素或者元素重复,随后该判断结束

然后判断e是否为空,若不为空则代表元素有重复(上面说过了e表示当前元素的下一个元素,若e不为空则表示出现了元素重复以至于跳出循环,若e为空则表示循环到了链表的最后而没有出现元素重复跳出循环的操作)此时需要执行覆盖操作,再次判断方法执行时onlyifAbsent变量是否为false(不保留)或者数组中的元素是否为空,满足则将新元素的值覆盖旧元素的值,然后调用afterNodeAccess方法将e传入(该方法在hashMap中方法体为空,是用于给子类LinkedHashMap类继承使用,我也不知到啥用)随后返回旧的值(这就是为什么put方法覆盖元素会返回被覆盖的值)判断结束

然后再一次判断数组的长度加一后是否大于哈希表的扩容时机,如果满足调用resize方法扩容不满足则什么都不作

最后返回null(表示没有覆盖任何元素)

resize方法中先判断数组是否为空,为空则创建一个长度 16默认因子为0.75的数组,不为空会判断是否满足扩容条件

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 是怎样实现的? HashMap的put方法底层实现是通过计算key的hashcode,将其存储到桶,如果桶已经存在该key,则替换value,如果桶不存在该key,则添加key-value对到桶。 ### 回答2: HashMap的put方法是用来将键值对存储到HashMap方法。其底层代码如下: 1. 首先,根据传入的键(key)计算出其哈希值(hash)。HashMap使用键的哈希值来确定其在内部数组的位置。 2. 根据哈希值计算出数组的索引位置(index)。通过对哈希值进行一系列的位运算,如与运算、取模运算等。 3. 到达索引位置后,会判断该位置是否已经有键值对。如果没有,则直接将传入的键值对存储在该位置,并标记该位置已经有键值对。如果有,进行下一步。 4. 若该位置已经有键值对,则需要进行链表或红黑树的操作HashMap内部使用链表和红黑树来解决哈希冲突的问题。先判断该位置上的数据结构是链表还是红黑树。如果是链表,则遍历链表,根据键的equals方法判断是否存在相同的键。如果存在相同的键,则更新键对应的值;如果没有相同的键,则将新的键值对添加到链表的末尾。 5. 如果该位置上的数据结构是红黑树,则根据键的hashCode和equals方法查找该位置上是否已经存在相同的键。如果存在相同的键,则更新键对应的值;如果没有相同的键,则通过红黑树的插入操作将新的键值对插入到红黑树。 6. 如果插入的键值对导致链表长度达到阈值(默认为8),或者红黑树节点个数达到阈值(默认为6),则会将链表转换为红黑树,或者扩容内部数组的大小。 以上就是HashMap的put方法底层代码。在实际使用,可以根据自己的需要来设置初始容量和负载因子,以便在更高的效率和更合适的条件下进行存储和访问。 ### 回答3: HashMap的put方法HashMap一个核心方法,用于将键值对插入到HashMap。下面是put方法底层代码: 1. 首先,根据传入的键计算哈希值,这个哈希值会被用来决定存储位置。 2. 接下来,通过对哈希值进行位运算,计算出应该存储数据的桶的索引位置。 3. 然后,检查该索引位置的桶是否为空。如果为空,表示该位置还没有存储数据,则直接将键值对插入到该位置,并将插入后的桶的数量加1。 4. 如果不为空,表示该位置已经存储了其他的键值对。此时,需要进行以下操作: - 遍历该位置的桶,将新的键值对与已存在的键值对进行比较。 - 如果键已经存在于HashMap,则将新的值覆盖旧的值,并返回旧的值。 - 如果键不存在于HashMap,则将新的键值对插入到桶,并将桶的数量加1。 - 如果桶存储的键值对数量达到一个阈值(默认是8),则需要对该桶进行树化处理,以提高查找效率。 5. 最后,如果插入操作成功,则返回null;如果插入操作导致键值对数量的改变,则返回旧的值。 总的来说,HashMap的put方法通过计算哈希值和位运算来确定存储位置,然后根据该位置是否为空进行插入或替换操作,最后根据插入操作的结果返回相应的值。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值