一、概述

  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的更多相关文章

  1. Java容器解析系列(14) IdentityHashMap详解

    IdentityHashMap,使用什么的跟HashMap相同,主要不同点在于: 数据结构:使用一个数组table来存储 key:value,table[2k] 为key, table[2k + 1] ...

  2. 【Java心得总结七】Java容器下——Map

    我将容器类库自己平时编程及看书的感受总结成了三篇博文,前两篇分别是:[Java心得总结五]Java容器上——容器初探和[Java心得总结六]Java容器中——Collection,第一篇从宏观整体的角 ...

  3. [Think In Java]基础拾遗3 - 容器、I/O、NIO、序列化

    目录 第十一章 持有对象第十七章 容器深入研究第十八章 Java I/O系统 第十一章 持有对象 1. java容器概览 java容器的两种主要类型(它们之间的主要区别在于容器中每个“槽”保存的元素个 ...

  4. Java - 容器详解

    一.ArrayList 长度可变数组,类似于c++ STL中的vector. 元素以线性方式连续存储,内部允许存放重复元素. 允许对元素进行随机的快速访问,但是向ArrayList中插入和删除元素的速 ...

  5. Java容器之旅:容器基础知识总结

    下图展示了Java容器类库的完备图,包括抽象类和遗留构件(不包括Queue的实现). 常用的容器用黑色粗线框表示,点线框表示接口,虚线框表示抽象类,实线框表示类,空心箭头表示实现关系.Produce表 ...

  6. Java集合容器简介

    Java集合容器主要有以下几类: 1,内置容器:数组 2,list容器:Vetor,Stack,ArrayList,LinkedList, CopyOnWriteArrayList(1.5),Attr ...

  7. Java 容器相关知识全面总结

    Java实用类库提供了一套相当完整的容器来帮助我们解决很多具体问题.因为我本身是一名Android开发者,包括我在内很多安卓开发,最拿手的就是ListView(RecycleView)+BaseAda ...

  8. Java基础:容器

    转载请注明出处:jiq•钦's technical Blog 一.Collection:存放独立元素 Collection中的接口都是可选操作,事实上现类 并不一定实现了其全部接口,这是为了防止&qu ...

  9. Java容器的各种总结

    Java容器指的是List,Set,Map这些类.由于翻译的问题,问到集合,Collection这些指的都是它们几个. List ArrayList 随机访问快 LinkedList 插入删除快 这个 ...

随机推荐

  1. Atitit.软件与编程语言中的锁机制原理attilax总结

    Atitit.软件与编程语言中的锁机制原理attilax总结 1. 用途 (Db,业务数据加锁,并发操作加锁.1 2. 锁得类型 排它锁 "互斥锁 共享锁 乐观锁与悲观锁1 2.1. 自旋锁 ...

  2. redis常用操作总结

    在项目中时常会用到redis,redis看起来好像很难的样子,而且我也确认反复学习了很久,但是,总结下来,自己使用到的东西并不太多,如下作一些总结工作. 1.安装(单机) 1.1 windows, 直 ...

  3. 【WP开发】认清“不透明度”与“可见性”的区别

    这两种情况,许多朋友平时都没有注意到: 1.设置Opacity属性的值为0: 2.将Visibility属性设置为Collapsed. 不少人会简单地认为这两种情况是一样的,都是让UI元素看不见. 我 ...

  4. IO流-ZIP文档

    java中通常使用ZipInputStream来读ZIP文档 ZIP文档(通常)以压缩格式存储了一个或多个文件,每个ZIP文档都有一个包含诸如文件 名字和所使用的压缩方法等信息的头.在Java中,可以 ...

  5. Android基于mAppWidget实现手绘地图(三)--环境搭建

    首先,你在Eclispe开发环境中新建一个项目:然后,可以通过Eclispe项目管理工具把这个项目集成到你的app项目中.下面手册会手把手教你如何正确创建一个地图对象. 第一: 新建一个新的Anroi ...

  6. Sparse Filtering 学习笔记(三)目标函数的建立和求解

      Sparse Filtering 是一个用于提取特征的无监督学习算法,与通常特征学习算法试图建模训练数据的分布的做法不同,Sparse Filtering 直接对训练数据的特征分布进行分析,在所谓 ...

  7. Python编码问题整理

    认识常见编码 GB2312是中国规定的汉字编码,也可以说是简体中文的字符集编码 GBK 是 GB2312的扩展 ,除了兼容GB2312外,它还能显示繁体中文,还有日文的假名 cp936:中文本地系统是 ...

  8. CSS3 过渡特性创建信封效果的联系表单

    最近给大家分享 CSS3 效果比较多,都是充分运用了 CSS3 来实现的.通过 CSS3 的新特性,我们创作出好的交互和效果的可能性大大增加.这篇文章中,我想与大家分享使用 CSS3 过渡特性实现的信 ...

  9. td中绝对定位div的垂直居中问题

    一.需求 典型的注册页面设计一般是一个三列多行的表格,左列为表单项label,中列为文本框,右列为填写说明和验证提示.如下图 因为整个表单的视觉重心在左中两列,所以在考虑表单整体相对页面水平居中和注册 ...

  10. Android聚合广告AFP的对接系统设计

    工作需要,要对接阿里妈妈的广告聚合平台,简称AFP.对于一般的应用而言,想要流量变现,广告是显而易见的手段,尤其是在中国,打开一个千万级别的用户,肯定有某个地方是有对接广告的,只不过明不明显而已. 阿 ...