IdentityHashMap,使用什么的跟HashMap相同,主要不同点在于:

  1. 数据结构:使用一个数组table来存储 key:value,table[2k] 为keytable[2k + 1] 为value,也即:
    key:value ==> table[2k]:table[2k + 1](HashMap使用数组 + 链表);
  2. IdentityHashMap 中的 key 和 value 通过==来比较是否相等(HashMap通过equals());
  3. 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的类图和底层数据结构是一样的,区别在于:

  1. IdentityHashMap使用的是==比较key的值,而HashMap使用的是equals()
  2. IdentityHashMap使用的是System.identityHashCode(object)查找桶的位置,HashMap使用的是hashCode()
  3. IdentityHashMap理论上来说速度要比HashMap快一点
  4. 由于IdentityHashMap的key比较的是引用,因此key的内容是可以重复的,但是HashMap是不会的。

JDK中IdentityHashMap使用详解相关推荐

  1. java做jsp问题_java/jsp中 中文问题详解

    java/jsp中 中文问题详解 更新时间:2006年10月13日 00:00:00   作者: 预备知识: 1.字节和unicode Java内核是unicode的,就连class文件也是,但是很多 ...

  2. JAVA中的Random详解

    JAVA中的Random详解 首先,在JDK自带的常用的random中有两个,这俩都是产生随机数的,不过一个是util下的random,另外一个是Math下的.我们分别介绍一下 util中的rando ...

  3. Java虚拟机中类加载机制详解

    Java虚拟机中类加载机制详解 1,什么是java类加载机制 **首先在java中,是通过编译来生成.class文件(可能在本地,或者网页下载),java的类加载机制就是 将这些.class文件加载到 ...

  4. idea中tomcat配置详解

    idea中tomcat配置详解 本篇文章主要介绍在idea下配置maven.tomcat.jdk的开发环境. 详细步骤: 1.Edit Configurations 2.Add New Tomcat ...

  5. ALSA声卡驱动中的DAPM详解之四:在驱动程序中初始化并注册widget和route

    前几篇文章我们从dapm的数据结构入手,了解了代表音频控件的widget,代表连接路径的route以及用于连接两个widget的path.之前都是一些概念的讲解以及对数据结构中各个字段的说明,从本章开 ...

  6. Asp.net中GridView使用详解(引)【转】

    Asp.net中GridView使用详解(引) GridView无代码分页排序 GridView选中,编辑,取消,删除 GridView正反双向排序 GridView和下拉菜单DropDownList ...

  7. Linux中iptraf命令详解(IP局域网监控工具)

    2019独角兽企业重金招聘Python工程师标准>>> Linux中iptraf命令详解(IP局域网监控工具) 发布时间:2017-12-27 20:46:03   作者:佚名    ...

  8. ArcGIS Engine中的Symbols详解

    转自原文 ArcGIS Engine中的Symbols详解 本文由本人翻译ESRI官方帮助文档.尊重劳动成果,转载请注明来源. Symbols ArcObjects用了三种类型的Symbol(符号样式 ...

  9. js路由在php上面使用,React中路由使用详解

    这次给大家带来React中路由使用详解,React中路由使用的注意事项有哪些,下面就是实战案例,一起来看一下. 路由 通过 URL 映射到对应的功能实现,React 的路由使用要先引入 react-r ...

  10. Linux中etc目录详解

    Linux中etc目录详解 /etc目录 包含很多文件.许多网络配置文件也在/etc 中. /etc/rc   or/etc/rc.d   or/etc/rc*.d   启动.或改变运行级时运行的sc ...

最新文章

  1. DOM4J对于XML的用法
  2. localization of FreeBSD
  3. 天津大学张梅山老师要招NLP方向的研究生啦!
  4. 事件捕获、冒泡、绑定、赋值、委托、兼容、滚轮
  5. 南京大学计算机复试离散数学,南京大学计算机复试线 历年南京大学计算机考研复试离散数学题集.doc...
  6. linux删除tmp文件找回,如何在 Linux 下快速找到被删除的文件?
  7. Linux中GCC编译工具集中个软件的用途、gcc的简单编译以及ELF文件格式
  8. 定个可以实现的小目标
  9. 计算机播放音乐无声音,笔记本电脑放歌没声音的解决方法
  10. 提现业务流程介绍与设计
  11. annotation 的方法
  12. Excel将其他单元格的数据合并成一个单元格
  13. ubuntu lotus testnet-staging
  14. 如何进行数据文件的传输(不简单)
  15. HighSpeedCharting简单的使用
  16. CSS略详细的基础 助你一臂之力
  17. Elastic Stack容器化部署拓展(Https、AD域集成)并收集Cisco设备的日志信息
  18. 项目中成功的运用proxool连接池
  19. AIRIOT物联网低代码平台如何配置Modbus RTU协议?
  20. 一份网站seo优化整体解决方案

热门文章

  1. 26. JavaScript 计时
  2. VMware 虚拟机运行卡慢的解决办法
  3. 【shell】一篇文章学懂Shell脚本
  4. (5)剑指Offer之栈变队列和栈的压入、弹出序列
  5. OsmocomBB编译及GSM嗅探问题(转)
  6. Java概 述(新手专区)
  7. Windows中MySQL主从数据库搭建(三)
  8. 导入项目后资源文件乱码---eclipse插件properties Editor安装
  9. Visual Studio附加调试进程时找不到
  10. C#、JS、HTML - 转义字符