容器--IdentityHashMap
一、概述
IdentityHashMap也是一类特殊的Map,根据其名字,Identity,即同一性,其表现出的具体特点便是,在判断Map中的两个key是否相等时,只通过==来判断,而不通过equals,也就是说,如果两个key相同,那么这两个key必须是同一个对象。
除此之外,虽然这也是HashMap,即至少是Key的存储是基于Hash实现的,但其存储方式跟HashMap还是有很大的不一样。下面详细分几个方面进行说明。
二、实现分析
1. 初始化
可以通过三种方式来构造一个IdentityHashMap, 如下:
public IdentityHashMap() {
init(DEFAULT_CAPACITY);//默认32
}
public IdentityHashMap(int expectedMaxSize) {
if (expectedMaxSize < 0)
throw new IllegalArgumentException("expectedMaxSize is negative: " + expectedMaxSize);
init(capacity(expectedMaxSize));
}
public IdentityHashMap(Map<? extends K, ? extends V> m) {
// Allow for a bit of growth
this((int) ((1 + m.size()) * 1.1));
putAll(m);
}
可以看到,以上三种方式最终都调用了init方法,而传入的参数即是容器的容量,而第二个方法中,在调用init之前会先对参数值进行处理,其目的是根据当前值构造一个最接近该数值的2的指数倍,这样,保证初始化时,容量的大小为2的指数倍。
初始化的具体过程如下:
private void init(int initCapacity) {
threshold = (initCapacity * 2)/3; //阈值为初始值的2/3,所以对于默认值32来说,
//其大小为32*2/3=21,这是key的个数
table = new Object[2 * initCapacity];//为什么*2,因为值也是放在这个table中的
//所以table的size不等于容量
}
代码很简单,设置阈值为容量的2/3,并申请一个2倍于容量的数组。之所以这里要扩大一倍,是因为Map值也存储于这个数组中,所以,需要与key一一对应。
2.存储
对于存储来说,我们还是来看下put的实现
public V put(K key, V value) {
Object k = maskNull(key);
Object[] tab = table;
int len = tab.length;
int i = hash(k, len);
Object item;
while ( (item = tab[i]) != null) {
if (item == k) {
V oldValue = (V) tab[i + 1];
tab[i + 1] = value;
return oldValue;
}
i = nextKeyIndex(i, len);
}
modCount++; //新增加一个
tab[i] = k;
tab[i + 1] = value;
if (++size >= threshold)
resize(len); // len == 2 * current capacity.
return null;
}
private static int nextKeyIndex(int i, int len) {
return (i + 2 < len ? i + 2 : 0);
}
根据上面的业务逻辑,我们将其用流程图表示如下:

根据流程图我们不难理解这个逻辑,有几点需要注意一下:
1)key和value的值实际上都是存储在数组中的,而且val是挨着key存储的。
2)当发生冲突的时候,这里采用的方式是依次找下一个,直到有空的位置,或者找到key应有的位置。
3)因为在超过阈值后会进行resize的操作,table的长度会被扩大一倍,所以步骤2)一定能找到一个空位置,或者找到之前设置的位置。
如果没有自动扩容机制,则步骤2)很可能会出现死循环。
3. 查找
get方法的实现逻辑如下:
public V get(Object key) {
Object k = maskNull(key);
Object[] tab = table;
int len = tab.length;
int i = hash(k, len);//表的长度,2的n次方
while (true) {
Object item = tab[i];
if (item == k)
return (V) tab[i + 1];
if (item == null)
return null; //那么这个表示不存在该key, 所以返回null
i = nextKeyIndex(i, len);
}
}
这个过程很简单,了解了存储机制后,这个就容易理解了,需要说明的是,如果该位置为null,表示该位置未存储key, 返回null.
4. 删除
删除的业务逻辑如下:
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;
closeDeletion(i);
return oldValue;
}
if (item == null) //未找到该key
return null;
i = nextKeyIndex(i, len);
}
}
查找的逻辑还是比较好理解,删除的话,实际上就是把相应位置的值置null,实现引用的消除,以便垃圾回收。
三、总结
上面我们大致分析了这类Map的存储机制,总的来说还是比较简单,和HashMap有类似的地方,但实现方式差别很大。主要差别整理如下 :
1)IdentityHashMap的loadFactor固定为2/3
2)IdentityHashMap的所有key和value都存储在数组中,key后的下一个位置即是对应的value
3)IdentityHashMap的冲突检测方式为线性再探测,即找下一个元素再探测,没有链式结构
4)最重要的一点,判断两个key是否相同,只根据==来判断,不使用equals
如果我们的业务需要有第4)点的需求,则可以使用IdentityHashMap.
容器--IdentityHashMap的更多相关文章
- Java容器解析系列(14) IdentityHashMap详解
IdentityHashMap,使用什么的跟HashMap相同,主要不同点在于: 数据结构:使用一个数组table来存储 key:value,table[2k] 为key, table[2k + 1] ...
- 【Java心得总结七】Java容器下——Map
我将容器类库自己平时编程及看书的感受总结成了三篇博文,前两篇分别是:[Java心得总结五]Java容器上——容器初探和[Java心得总结六]Java容器中——Collection,第一篇从宏观整体的角 ...
- [Think In Java]基础拾遗3 - 容器、I/O、NIO、序列化
目录 第十一章 持有对象第十七章 容器深入研究第十八章 Java I/O系统 第十一章 持有对象 1. java容器概览 java容器的两种主要类型(它们之间的主要区别在于容器中每个“槽”保存的元素个 ...
- Java - 容器详解
一.ArrayList 长度可变数组,类似于c++ STL中的vector. 元素以线性方式连续存储,内部允许存放重复元素. 允许对元素进行随机的快速访问,但是向ArrayList中插入和删除元素的速 ...
- Java容器之旅:容器基础知识总结
下图展示了Java容器类库的完备图,包括抽象类和遗留构件(不包括Queue的实现). 常用的容器用黑色粗线框表示,点线框表示接口,虚线框表示抽象类,实线框表示类,空心箭头表示实现关系.Produce表 ...
- Java集合容器简介
Java集合容器主要有以下几类: 1,内置容器:数组 2,list容器:Vetor,Stack,ArrayList,LinkedList, CopyOnWriteArrayList(1.5),Attr ...
- Java 容器相关知识全面总结
Java实用类库提供了一套相当完整的容器来帮助我们解决很多具体问题.因为我本身是一名Android开发者,包括我在内很多安卓开发,最拿手的就是ListView(RecycleView)+BaseAda ...
- Java基础:容器
转载请注明出处:jiq•钦's technical Blog 一.Collection:存放独立元素 Collection中的接口都是可选操作,事实上现类 并不一定实现了其全部接口,这是为了防止&qu ...
- Java容器的各种总结
Java容器指的是List,Set,Map这些类.由于翻译的问题,问到集合,Collection这些指的都是它们几个. List ArrayList 随机访问快 LinkedList 插入删除快 这个 ...
随机推荐
- SqlServer 错误1053:服务并未及时响应启动或控制请求
sqlserver 的登录用户修改成域账户后,启动不了 解决方法: 计算器管理选择管理员组 将域账户加入到管理员组即可
- salesforce 零基础学习(二十三)数据记录导出至excel(自定义报表导出)
我们都知道,报表有个功能为导出excel,但是有的时候客户需求往往标准的报表达不到,比如导出excel,其中本月修改的数据字段标红,如下图所示. 这就需要我们去写VF来实现此功能. 需求:将数据表记录 ...
- 阿里云上安装mysql步骤/ 阿里云ECS搭建Java+mysql+tomcat环境
使用阿里云ECS挺长一段时间了.这两天碰巧朋友小白让我一步一步教他在ECS上搭建Java+mysql+tomcat环境,所以把在这里把步骤在这简单整理了一下,以便需要的人查阅. 我购买的阿里云服务器系 ...
- EasyUI Field
效果: JS: var sortIndex = $("#ListDiv").find(".datagrid-view2").find(".datagr ...
- Python下划线与命名规范
Python下划线与命名规范 先看结论,节省只想知道答案你的宝贵时间: _xxx 不能用于from module import * 以单下划线开头的表示的是protected类型的变量.即保护类型只能 ...
- gulp学习笔记4
gulp系列学习笔记: 1.gulp学习笔记1 2.gulp学习笔记2 3.gulp学习笔记3 4.gulp学习笔记4 之前的任务都是单个的,比较简单.接下去我们开始引用多个插件,一次性把任务搞定,省 ...
- echart饼状图使用,打发时间。
新公司,刚来几天,闲着没事,领导让我做些无关痛痒的活,优化报表统计!!!之前是用flash做的,现在要改成echart实现.好吧,之前没用过,抱着学习态度,研究了下.写点东西打发下时间,能帮到需要帮助 ...
- 邻接矩阵无向图(一)之 C语言详解
本章介绍邻接矩阵无向图.在"图的理论基础"中已经对图进行了理论介绍,这里就不再对图的概念进行重复说明了.和以往一样,本文会先给出C语言的实现:后续再分别给出C++和Java版本的实 ...
- Tomcat7.0启动报错:java.lang.illegalargumentexception:taglib definition not consisten with specification version
Tomcat7.0启动报错:java.lang.illegalargumentexception:taglib definition not consisten with specification ...
- SQLServer学习笔记系列3
一.写在前面的话 今天又是双休啦!生活依然再继续,当你停下来的时候,或许会突然显得不自在.有时候,看到一种东西,你会发现原来在这个社会上,优秀的人很多,默默 吃苦努力奋斗的人也多!星期五早上按时上班, ...