HashMap在JDK1.7版本头插法实现解析

article/2025/9/30 3:33:11

                      HashMap在JDK1.7版本头插法实现解析

先解释下何为头插法。大家都知道HashMap在JDK1.7版本的数据结构为数组+链表这样的形式。而头插法说的就是在往HashMap里面put元素时,此时新增在链表上元素的位置为链表头部,也就是数组桶位上的那个位置,故名头插法。

直接上源码,看看JDK1.7的元素插入代码实现:

 

    public V put(K key, V value) {// 步骤1if (table == EMPTY_TABLE) {inflateTable(threshold);}// 步骤2if (key == null)return putForNullKey(value);// 步骤3int hash = hash(key);int i = indexFor(hash, table.length);for (Entry<K,V> e = table[i]; e != null; e = e.next) {Object k;if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {V oldValue = e.value;e.value = value;e.recordAccess(this);return oldValue;}}modCount++;addEntry(hash, key, value, i);return null;}

稍微解释下前面代码流程:
1、如果往HashMap里面put元素的时候,发现还是空的未初始化(HashMap采用懒加载,用到的时候再去初始化),那就调用inflateTable(threshold)先给它初始化了先。
2、如果put元素的时候key为null,则进入putForNullKey(value)方法处理,大概逻辑就是将key为0的key-value放入entry,如果之前已有key为null的key-value键值对,则返回原value,否则返回null。
3、接下来就是取key的hash再做位操作(之所以不直接使用hashCode是为了加大低位信息的随机性,变相让高位数据参与到计算中)、然后获取元素key值运算得到数组的下标,最后再判断是否有相同key存在于entry,有就新value覆盖旧value然后返回旧value。

直到addEntry(hash, key, value, i)才是头插法的实现开始:

    void addEntry(int hash, K key, V value, int bucketIndex) {if ((size >= threshold) && (null != table[bucketIndex])) {resize(2 * table.length);hash = (null != key) ? hash(key) : 0;bucketIndex = indexFor(hash, table.length);}createEntry(hash, key, value, bucketIndex);}

if里面判断是否需要扩容,可先忽略接着看 createEntry(hash, key, value, bucketIndex):

    void createEntry(int hash, K key, V value, int bucketIndex) {Entry<K,V> e = table[bucketIndex];table[bucketIndex] = new Entry<>(hash, key, value, e);size++;}

大家注意这个参数bucketIndex,它是之前用key的哈希值做过位运算之后再去找数组运算得到的下标。如果要讲key-value这个键值对放入hashmap的话,就会放到数组的这个位置或者这个位置的链表上。

Entry<K,V> e = table[bucketIndex]这一句则取到数组上这个下标的元素,然后作为new Entry<>(hash, key, value, e)的参数e:

    Entry(int h, K k, V v, Entry<K,V> n) {value = v;next = n;key = k;hash = h;}

从代码可以得知,这个构造方法,利用key-value构造了一个entry,然后把它的next属性(类似链表的后继节点)指向原table[bucketIndex],然后在上一层方法中复制给了table[bucketIndex],实际效果就是想当于把该下标下的链表整体往下移了一部,再用新构造的entry放在链表头(同时也是数组上该下标的位置)。这就是jdk1.7版本hashmap元素在put时的头插法。

 

至于为什么会采用头插法,据说是考虑到热点数据的原因,即最近插入的元素也很可能最近会被使用到。所以为了缩短链表查找元素的时间,所以每次都会将新插入的元素放到表头。

这里再稍微拓展下,大家都知道数组查找元素快,而插入或删除元素慢;而链表恰恰相反,查找元素慢,插入或删除快。这是因为两个的数据结构不同而导致的。
数组因为有下标的存在,可以直接根据下标定位到相应元素。而在插入元素或删除元素时,却需要移动该元素后面所有的元素,所以开销会比较大。
而链表没有下标的存在,想要查找元素只能从头结点顺着往下找,若链表非常长且目标元素恰巧在链表尾部,花费的时间相对而言也不短了。同时链表有前继节点与后继节点的存在,当需要插入或删除元素时,只需要修改两个节点的前继节点与后继节点的指向就行了,这也是为什么链表新增或删除元素要比数组快的原因。

在这里插入图片描述


http://chatgpt.dhexx.cn/article/iLPBs788.shtml

相关文章

头插法链表反转c语言,用头插法反转链表

题目&#xff1a;输入一个链表的头结点&#xff0c;反转该链表&#xff0c;并返回反转后链表的头结点。 链表结点定义如下&#xff1a; typedef char item_t; typedef struct node { item_t item; struct node * next; } node_t; 分析&#xff1a; 使用头插法可以快速实现反转。…

链表头插法

头插法 从一个空表头指针开始&#xff0c;重复读入数据&#xff0c;生成新节点&#xff0c; 将读入数据存放到新节点的数据域中&#xff0c;永远是将新节点插入到当前链表的头节点的后面&#xff0c;第一个创建的节点是放在最后的&#xff0c;直到读入结束标志才停止创建。 #in…

HashMap头插法

HashMap在1.8&#xff08;不含&#xff09;之前对于新增元素的hash冲突的链表插入采用的是头插法&#xff0c;1.8之后开始改用尾插法。那么头插法有什么问题呢&#xff1f;为什么改用尾插法呢&#xff1f;源码学习一下咯 HashMap-jdk1.7.0_80 put新增map元素 public V put(K…

(最详细)c语言尾插法头插法代码讲解

1.尾插法 尾插法 头指针和尾指针都指向头结点&#xff0c;然后往里边插入元素&#xff0c; 每插入一个元素尾指针就后移一下 其中如下图所示 尾插法的核心代码是&#xff1a; pointer->next s; //pointer指向新生成的节点 pointer pointer->next;//pointer移动至新…

头插法和尾插法总结(动图版)

代码使用结构体&#xff1a; typedef struct Node{int value;struct Node* next; }*Link;头插法&#xff1a;利用头指针控制链表节点的增加。 核心&#xff1a; newNode->next head->next; head->next newNode; //头插法创建链表 Link headCreateLink(int n){//头指…

头插法和尾插法建立单链表详解与实现

写在前面&#xff1a;本文使用C语言和C引用&#xff0c;学C和C的同学都是可以看懂的&#xff0c;C毕竟向下兼容C。很详细&#xff0c;一篇能搞懂代码和原理。 先来了解几个简单概念 单链表就是线性表的链式存储&#xff1b; 头结点&#xff1a;单链表在第一个结点之前附加了一个…

链表的三种插入方法(头插法,尾插法,任意位置插入)

插入作为链表的四大基本操作之一&#xff08;增删改查&#xff09;&#xff0c;通常都会借助插入的方法增添信息&#xff0c;这一部分为大家着重讲解插入法。 1.头插法 简而言之&#xff0c;就是从链表的头部进行一个插入&#xff0c;定义一个结构体指针的新节点&#xff0c;…

【数据结构】:单链表之头插法和尾插法(动图+图解)

头插法和尾插法 一、头插法&#x1f4a4;思考一&#xff1a;头插法的核心是什么❓❗❗ 重点一&#xff1a;以带头结点方式实现头插法❗❗ 重点二&#xff1a;以不带头结点方式实现头插法 二、尾插法&#x1f4a4;思考二&#xff1a;尾插法的核心是什么❓❗❗ 重点三&#xff1a…

链表:头插法与尾插法(简易图解和代码)

头插法 定义&#xff1a;输入的数据次序生成的链表节点次序相反&#xff0c;例如&#xff1a;按1,2,3顺序进行头插之后&#xff0c;最终排序却变成了3,2,1。简而言之就是逆序插入。 定义图解&#xff1a; 代码图解&#xff1a; 代码&#xff1a;&#xff08;使用头插法建立单…

未声明的标识符

出现未声明的标识符错误&#xff0c;有可能是真的没有声明。但是也有可能是代码中有些注释&#xff0c;格式不对&#xff0c;导致声明的变量的作用域提前结束了。可以把注释都删掉试试

c2065 未声明的标识符 解决ok

场景1&#xff1a; cv::Mat pad_img; cv::Mat img; img 在一个if else后面声明&#xff0c;就报错了 解决方法&#xff0c;把cv::Mat 声明放到前面就行了&#xff0c;原因未知。 场景2&#xff1a; int a3; a为未声明的标识符&#xff0c;加了各种头文件&#xff0c;都不起…

已经包含头文件仍然出现,错误C2065“未声明的标识符”

由于当前在往一个比较大的项目中添加文件&#xff0c;文件又有相似性所以采取了复制的方式&#xff0c;最后出现了一个大疏漏。 在总的.cpp文件中调用新文件中的函数&#xff0c;在包含了新文件的.h头文件的情况下仍然说没有找到标识符&#xff0c;在网上找了很多方法&#xff…

c++: “default”: 未声明的标识符

c&#xff1a; “default”: 未声明的标识符 c&#xff1a; “default”: 未声明的标识符 1、错误描述 2、错误原因&#xff1a; 3、解决方案&#xff1a; 1、错误描述 错误 C2065 “default”: 未声明的标识符 2、错误原因&#xff1a; C11 标准的新特性…

C++命名空间namspace解析——“cout”未声明的标识符,“cin”未声明的标识符

首先我们先看一下下面这段代码运行时的情况&#xff08;注意按ctrlF5 运行&#xff09; #include<iostream> int main() {return 0; }运行结果如下 是一个没有任何结果的窗口 现在我们再加上一段输出代码 cout<<"hello"<<endl;会发现编译运行时…

error C2065: “string”: 未声明的标识符//error C2065: “vector”: 未声明的标识符

添加头文件没&#xff0c; #include <stdio.h> #include <string.h> #include #include using std::vector; #include <stdio.h> #include <string.h> #include <iostream> #include <vector> using std::vector;就这样&#xff0c;先&…

使用控件时提示未声明标识符的解决方法

环境&#xff1a;VS2012&#xff0c;Win8.1 64bit 参考&#xff1a; http://www.cnblogs.com/Romi/archive/2012/01/06/2314390.html http://zhidao.baidu.com/question/256304139.html 1. 用MFC默认创建Dialog类型项目&#xff0c;里面有个VS默认添加的CStatic控件 2. 如果…

引入头文件结构体,解决未声明标识符fileinfo

首先我问大佬&#xff0c;大佬让我右键单击结构体_finddata_t,找下它的定义&#xff0c;但是我没找到啥。然后大佬说让我引入结构体所在的头文件。我就右键单击_finddata_t&#xff0c;联机搜索&#xff0c;找到了它所在的头文件io.h&#xff0c;成功解决了未声明标识符fileinf…

未声明标识符怎么解决oracle,请问 什么是“未声明的标识符”错误,如何解决?...

月关宝盒 它们通常来自忘记包含包含函数声明的头文件&#xff0c;例如&#xff0c;此程序将给出“未声明的标识符”错误&#xff1a;缺少标题int main() { std::cout < return 0;}要修复它&#xff0c;我们必须包含标题&#xff1a;#include int main() { std::cout < re…

vs编译运行报错:未声明的标识符

报错如下图&#xff1a; 很奇怪&#xff0c;我这个testList是已经声明的&#xff0c;就在上一行&#xff0c;但是还是会给我报错&#xff1a;未声明的标识符。 最后发现原因是在报warning的地方。 解决办法&#xff1a; 在代码处右击出现菜单栏&#xff0c;点击最下方的【保存…

未声明标识符怎么解决oracle,什么是“未声明的标识符”错误,如何解决?

潇潇雨雨 它们通常来自忘记包含包含函数声明的头文件&#xff0c;例如&#xff0c;此程序将给出“未声明的标识符”错误&#xff1a;缺少标题int main() { std::cout < return 0;}要修复它&#xff0c;我们必须包含标题&#xff1a;#include int main() { std::cout < re…