JDK中IdentityHashMap使用详解
IdentityHashMap
,使用什么的跟HashMap
相同,主要不同点在于:
- 数据结构:使用一个数组
table
来存储key:value
,table[2k]
为key
,table[2k + 1]
为value
,也即:
key:value ==> table[2k]:table[2k + 1
](HashMap
使用数组 + 链表); IdentityHashMap
中的key
和value
通过==
来比较是否相等(HashMap
通过equals()
);IdentityHashMap
中的 hash冲突解决方式为线性探测法
(HashMap
为拉链法
);
具体,我们来看关键源码:
/*** 数据存储结构:* 使用一个数组table来存储 key - value,第 table[2k] 为key, table[2k + 1] 为value,也即:* key:value ==> table[2k]:table[2k + 1]* IdentityHashMap 中的 key 和 value 通过 "==" 来比较是否相等(HashMap通过equals()来比较是否相等)* @since 1.4*/
public class IdentityHashMap<K, V> extends AbstractMap<K, V> implements Map<K, V>, java.io.Serializable, Cloneable {private static final int DEFAULT_CAPACITY = 32;private static final int MINIMUM_CAPACITY = 4;private static final int MAXIMUM_CAPACITY = 1 << 29;private transient Object[] table;// 存储键值对的数组private int size;private transient int modCount;private transient int threshold;private static final Object NULL_KEY = new Object();// 如果key为null,使用NULL_KEY代替private static Object maskNull(Object key) {return (key == null ? NULL_KEY : key);}// 如果之前key为null,被替换为NULL_KEY,现在替换回来private static Object unmaskNull(Object key) {return (key == NULL_KEY ? null : key);}public IdentityHashMap() {init(DEFAULT_CAPACITY);}// 各种构造器方法,省略......// 构造器中都会调用该方法private void init(int initCapacity) {threshold = (initCapacity * 2) / 3;// 因为键值存储在同一个数组中,所有数组大小为初始容量的2倍table = new Object[2 * initCapacity];}private static int hash(Object x, int length) {// 使用System.identityHashCode(x)计算hash值int h = System.identityHashCode(x);// 扰动;并且该表达式保证了元素的散列值是偶数return ((h << 1) - (h << 8)) & (length - 1);}// hash冲突解决方式:线性探测法(linear-probe);因为key后面立马是value,这里线性探测每次递增2;// 并且这里实现了循环探测;private static int nextKeyIndex(int i, int len) {return (i + 2 < len ? i + 2 : 0);}public V get(Object key) {Object k = maskNull(key);Object[] tab = table;int len = tab.length;int i = hash(k, len);while (true) {Object item = tab[i];if (item == k)return (V) tab[i + 1];// 如果第i个位置存储为key,第i+1位置则存储为对应的valueif (item == null)return null;// 线性探测i = nextKeyIndex(i, len);}}public boolean containsValue(Object value) {Object[] tab = table;for (int i = 1; i < tab.length; i += 2)// value也使用"=="判断相等if (tab[i] == value && tab[i - 1] != null)return true;return false;}public V put(K key, V value) {// 这里必须为 null 提供mask,因为在进行遍历,get()等操作时,如果遇到key为null,则认为该位置没有键值对Object k = maskNull(key);Object[] tab = table;int len = tab.length;int i = hash(k, len);Object item;// key为null,则认为该位置没有键值对while ((item = tab[i]) != null) {if (item == k) {// 通过"=="判断key相同V oldValue = (V) tab[i + 1];tab[i + 1] = value;return oldValue;}i = nextKeyIndex(i, len);// 线性探测}modCount++;// 如果第i个位置存储为key,第i+1位置则存储为对应的valuetab[i] = k;tab[i + 1] = value;if (++size >= threshold)resize(len); // len == 2 * current capacity.return null;}// 每次扩容为原来的2倍private void resize(int newCapacity) {int newLength = newCapacity * 2;Object[] oldTable = table;int oldLength = oldTable.length;if (oldLength == 2 * MAXIMUM_CAPACITY) { // can't expand any furtherif (threshold == MAXIMUM_CAPACITY - 1)throw new IllegalStateException("Capacity exhausted.");threshold = MAXIMUM_CAPACITY - 1; // Gigantic map!return;}if (oldLength >= newLength)return;Object[] newTable = new Object[newLength];threshold = newLength / 3;// 对每个键值对重新进行散列到新数组中for (int j = 0; j < oldLength; j += 2) {Object key = oldTable[j];if (key != null) {Object value = oldTable[j + 1];oldTable[j] = null;oldTable[j + 1] = null;int i = hash(key, newLength);while (newTable[i] != null)i = nextKeyIndex(i, newLength);newTable[i] = key;newTable[i + 1] = value;}}table = newTable;}public V remove(Object key) {Object k = maskNull(key);Object[] tab = table;int len = tab.length;int i = hash(k, len);while (true) {Object item = tab[i];if (item == k) {modCount++;size--;V oldValue = (V) tab[i + 1];tab[i + 1] = null;tab[i] = null;// 第i个位置的键值对移除了,空出了位置,看后面是否有键值对要填补这个位置(有些键值对是因为线性探测导致其位置偏离了其hash值)closeDeletion(i);return oldValue;}if (item == null)return null;i = nextKeyIndex(i, len);}}// 指定位置的键值对移除了,空出了位置,看后面是否有键值对要填补这个位置(有些键值对是因为线性探测导致其位置偏离了其hash值)private void closeDeletion(int d) {// Adapted from Knuth Section 6.4 Algorithm RObject[] tab = table;int len = tab.length;Object item;// d:空出来的键值对位置// i:当前处理的键值对位置// 直到key==null时才退出循环for (int i = nextKeyIndex(d, len); (item = tab[i]) != null; i = nextKeyIndex(i, len)) {int r = hash(item, len);// i != r:证明item是被线性探测后放置在位置i;// 这种情况 在插入时,线性探测出现了循环// i<r:item通过线性探测到前面位置r前面去了// ___i____r_____d____ i<r<=d// ___d____i_____r____ d<=i<r// 这个表达式相当于 (i < r) && !(i < d && d < r),也即 __i__d__r__(这种情况不能处理,因为这种情况下,因为线性探测是向后循环探测)if ((i < r && (r <= d || d <= i))||(r <= d && d <= i)// 表示情况:___r____d____i____,建议先理解这种更简单的情况) {// 把item对应的键值对放置到位置d;位置i为新空出来的键值对位置;tab[d] = item;tab[d + 1] = tab[i + 1];tab[i] = null;tab[i + 1] = null;d = i;}}}public void clear() {modCount++;Object[] tab = table;for (int i = 0; i < tab.length; i++)tab[i] = null;size = 0;}// Object方法代码,省略// 常用Map代码,省略// 序列化相关代码,省略}
IdentityHashMap并不是继承HashMap,它和HashMap的类图和底层数据结构是一样的,区别在于:
- IdentityHashMap使用的是==比较key的值,而HashMap使用的是equals()
- IdentityHashMap使用的是System.identityHashCode(object)查找桶的位置,HashMap使用的是hashCode()
- IdentityHashMap理论上来说速度要比HashMap快一点
- 由于IdentityHashMap的key比较的是引用,因此key的内容是可以重复的,但是HashMap是不会的。
JDK中IdentityHashMap使用详解相关推荐
- java做jsp问题_java/jsp中 中文问题详解
java/jsp中 中文问题详解 更新时间:2006年10月13日 00:00:00 作者: 预备知识: 1.字节和unicode Java内核是unicode的,就连class文件也是,但是很多 ...
- JAVA中的Random详解
JAVA中的Random详解 首先,在JDK自带的常用的random中有两个,这俩都是产生随机数的,不过一个是util下的random,另外一个是Math下的.我们分别介绍一下 util中的rando ...
- Java虚拟机中类加载机制详解
Java虚拟机中类加载机制详解 1,什么是java类加载机制 **首先在java中,是通过编译来生成.class文件(可能在本地,或者网页下载),java的类加载机制就是 将这些.class文件加载到 ...
- idea中tomcat配置详解
idea中tomcat配置详解 本篇文章主要介绍在idea下配置maven.tomcat.jdk的开发环境. 详细步骤: 1.Edit Configurations 2.Add New Tomcat ...
- ALSA声卡驱动中的DAPM详解之四:在驱动程序中初始化并注册widget和route
前几篇文章我们从dapm的数据结构入手,了解了代表音频控件的widget,代表连接路径的route以及用于连接两个widget的path.之前都是一些概念的讲解以及对数据结构中各个字段的说明,从本章开 ...
- Asp.net中GridView使用详解(引)【转】
Asp.net中GridView使用详解(引) GridView无代码分页排序 GridView选中,编辑,取消,删除 GridView正反双向排序 GridView和下拉菜单DropDownList ...
- Linux中iptraf命令详解(IP局域网监控工具)
2019独角兽企业重金招聘Python工程师标准>>> Linux中iptraf命令详解(IP局域网监控工具) 发布时间:2017-12-27 20:46:03 作者:佚名 ...
- ArcGIS Engine中的Symbols详解
转自原文 ArcGIS Engine中的Symbols详解 本文由本人翻译ESRI官方帮助文档.尊重劳动成果,转载请注明来源. Symbols ArcObjects用了三种类型的Symbol(符号样式 ...
- js路由在php上面使用,React中路由使用详解
这次给大家带来React中路由使用详解,React中路由使用的注意事项有哪些,下面就是实战案例,一起来看一下. 路由 通过 URL 映射到对应的功能实现,React 的路由使用要先引入 react-r ...
- Linux中etc目录详解
Linux中etc目录详解 /etc目录 包含很多文件.许多网络配置文件也在/etc 中. /etc/rc or/etc/rc.d or/etc/rc*.d 启动.或改变运行级时运行的sc ...
最新文章
- DOM4J对于XML的用法
- localization of FreeBSD
- 天津大学张梅山老师要招NLP方向的研究生啦!
- 事件捕获、冒泡、绑定、赋值、委托、兼容、滚轮
- 南京大学计算机复试离散数学,南京大学计算机复试线 历年南京大学计算机考研复试离散数学题集.doc...
- linux删除tmp文件找回,如何在 Linux 下快速找到被删除的文件?
- Linux中GCC编译工具集中个软件的用途、gcc的简单编译以及ELF文件格式
- 定个可以实现的小目标
- 计算机播放音乐无声音,笔记本电脑放歌没声音的解决方法
- 提现业务流程介绍与设计
- annotation 的方法
- Excel将其他单元格的数据合并成一个单元格
- ubuntu lotus testnet-staging
- 如何进行数据文件的传输(不简单)
- HighSpeedCharting简单的使用
- CSS略详细的基础 助你一臂之力
- Elastic Stack容器化部署拓展(Https、AD域集成)并收集Cisco设备的日志信息
- 项目中成功的运用proxool连接池
- AIRIOT物联网低代码平台如何配置Modbus RTU协议?
- 一份网站seo优化整体解决方案