容器--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 插入删除快 这个 ...
随机推荐
- 体验Visual Studio 2015 Windows Forms应用程序开发与维护
昨天到半夜还没有等到Visual Studio 2015的下载地址,实在熬不住就先休息了.北美地区的时区比北京时间要晚一些,今天早上到公司就看到Visual Studio 2015的下载地址,迅速的将 ...
- poj 3101Astronomy(圆周追击+分数最小公倍数)
/* 本题属于圆周追击问题: 假设已知两个圆周运动的物体的周期分别是a ,b, 设每隔时间t就会在同一条直线上 在同一条直线上的条件是 角度之差为 PI ! 那么就有方程 (2PI/a - 2PI/b ...
- 后端码农谈前端(CSS篇)第五课:CSS样式
一.背景: CSS 允许应用纯色作为背景,也允许使用背景图像创建相当复杂的效果. 1.背景色 可以使用 background-color 属性为元素设置背景色.这个属性接受任何合法的颜色值. 例如: ...
- 面试准备 - 最大堆的Csharp实现
面试中最常见的问题之一...在N个数中间寻找前K大个元素 最常见的解法就是最大堆 时间复杂度O(N*log(K)) 空间复杂度O(k) 实现了一个最简单的最大堆,每次有元素进来都和堆顶元素比较一下,如 ...
- Dijkstra算法(二)之 C++详解
本章是迪杰斯特拉算法的C++实现. 目录 1. 迪杰斯特拉算法介绍 2. 迪杰斯特拉算法图解 3. 迪杰斯特拉算法的代码说明 4. 迪杰斯特拉算法的源码 转载请注明出处:http://www.cnbl ...
- 邻接矩阵有向图(一)之 C语言详解
本章介绍邻接矩阵有向图.在"图的理论基础"中已经对图进行了理论介绍,这里就不再对图的概念进行重复说明了.和以往一样,本文会先给出C语言的实现:后续再分别给出C++和Java版本的实 ...
- [OpenCV] Samples 02: [ML] kmeans
注意Mat作为kmeans的参数的含义. 扩展:高维向量的聚类. #include "opencv2/highgui.hpp" #include "opencv2/cor ...
- [转载]AxureRP常用快捷键
习惯用Axure快捷键会让你做原型的时候更得心应手.Axure中文网总结了常用的一些快捷键分享给大家 . Axure RP Pro 6.5快捷键大全,如有疏漏,欢迎补充. 基本快捷键: 打开: ...
- 再次用CodeIgniter实现简易blog
天变冷了,人也变得懒了不少,由于工作的需要,最近一直在学习CodeIgniter(CI)框架的使用,没有系统的从PHP基本语法学起,在网上靠百度谷歌,东拼西凑的实现了一些简单的功能.所以,老PHPer ...
- java io系列16之 PrintStream(打印输出流)详解
本章介绍PrintStream以及 它与DataOutputStream的区别.我们先对PrintStream有个大致认识,然后再深入学习它的源码,最后通过示例加深对它的了解. 转载请注明出处:htt ...