一、前言

  前面已经分析了HashMap与LinkedHashMap,现在我们来分析不太常用的IdentityHashMap,从它的名字上也可以看出来用于表示唯一的HashMap,仔细分析了其源码,发现其数据结构与HashMap使用的数据结构完全不同,因为在继承关系上面,他们两没有任何关系。下面,进入我们的分析阶段。

二、IdentityHashMap示例  

import java.util.Map;
import java.util.HashMap;
import java.util.IdentityHashMap;public class IdentityHashMapTest {public static void main(String[] args) {Map<String, String> hashMaps = new HashMap<String, String>();Map<String, String> identityMaps = new IdentityHashMap<String, String>();hashMaps.put(new String("aa"), "aa");hashMaps.put(new String("aa"), "bb");identityMaps.put(new String("aa"), "aa");identityMaps.put(new String("aa"), "bb");System.out.println(hashMaps.size() + " : " + hashMaps);System.out.println(identityMaps.size() + " : " + identityMaps);}
}

View Code

  运行结果:

1 : {aa=bb}
2 : {aa=bb, aa=aa}  

说明:IdentityHashMap只有在key完全相等(同一个引用),才会覆盖,而HashMap则不会。

三、IdentityHashMap数据结构

  说明:IdentityHashMap的数据很简单,底层实际就是一个Object数组,在逻辑上需要看成是一个环形的数组,解决冲突的办法是:根据计算得到散列位置,如果发现该位置上已经有元素,则往后查找,直到找到空位置,进行存放,如果没有,直接进行存放。当元素个数达到一定阈值时,Object数组会自动进行扩容处理。

四、IdentityHashMap源码分析

  4.1 类的继承关系 

public class IdentityHashMap<K,V>extends AbstractMap<K,V>implements Map<K,V>, java.io.Serializable, Cloneable

  说明:继承了AbstractMap抽象类,实现了Map接口,可序列化接口,可克隆接口。

  4.2 类的属性  

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;// 用于存储实际元素的表transient Object[] table;// 大小int size;// 对Map进行结构性修改的次数transient int modCount;// null key所对应的值static final Object NULL_KEY = new Object();
}

View Code

  说明:可以看到类的底层就是使用了一个Object数组来存放元素。

  4.3 类的构造函数

  1. IdentityHashMap()型构造函数  

public IdentityHashMap() {init(DEFAULT_CAPACITY);
}

View Code

  2. IdentityHashMap(int)型构造函数

public IdentityHashMap(int expectedMaxSize) {if (expectedMaxSize < 0)throw new IllegalArgumentException("expectedMaxSize is negative: "+ expectedMaxSize);init(capacity(expectedMaxSize));}

View Code

  3. IdentityHashMap(Map<? extends K, ? extends V>)型构造函数

public IdentityHashMap(Map<? extends K, ? extends V> m) {// 调用其他构造函数this((int) ((1 + m.size()) * 1.1));putAll(m);}

View Code

  4.4 重要函数分析

  1. capacity函数 

// 此函数返回的值是最小大于expectedMaxSize的2次幂private static int capacity(int expectedMaxSize) {// assert expectedMaxSize >= 0;return(expectedMaxSize > MAXIMUM_CAPACITY / 3) ? MAXIMUM_CAPACITY :(expectedMaxSize <= 2 * MINIMUM_CAPACITY / 3) ? MINIMUM_CAPACITY :Integer.highestOneBit(expectedMaxSize + (expectedMaxSize << 1));}

View Code

  说明: 此函数返回的值是最小的且大于expectedMaxSize的2次幂的值。

  2. hash函数 

// hash函数,由于length总是为2的n次幂,所以 & (length - 1)相当于对length取模private static int hash(Object x, int length) {int h = System.identityHashCode(x);// Multiply by -127, and left-shift to use least bit as part of hashreturn ((h << 1) - (h << 8)) & (length - 1);}

View Code

  说明:hash函数用于散列,并且保证元素的散列值会在数组偶次索引。

  3. get函数 

public V get(Object key) {// 保证null的key会转化为Object(NULL_KEY)Object k = maskNull(key);// 保存tableObject[] tab = table;int len = tab.length;// 得到key的散列位置int i = hash(k, len);// 遍历table,解决散列冲突的办法是若冲突,则往后寻找空闲区域while (true) {Object item = tab[i];// 判断是否相等(地址是否相等)if (item == k)// 地址相等,即完全相等的两个对象return (V) tab[i + 1];// 对应散列位置的元素为空,则返回空if (item == null)return null;// 取下一个Key索引i = nextKeyIndex(i, len);}}

View Code

  说明:该函数比较key值是否完全相同(对象类型则是否为同一个引用,基本类型则是否内容相等)

  4. nextKeyIndex函数 

// 下一个Key索引private static int nextKeyIndex(int i, int len) {// 往后移两个单位return (i + 2 < len ? i + 2 : 0);}

View Code

  说明:此函数用于发生冲突时,取下一个位置进行判断。

  5. put函数  

public V put(K key, V value) {// 保证null的key会转化为Object(NULL_KEY)final Object k = maskNull(key);retryAfterResize: for (;;) {final Object[] tab = table;final int len = tab.length;int i = hash(k, len);for (Object item; (item = tab[i]) != null;i = nextKeyIndex(i, len)) {if (item == k) { // 经过hash计算的项与key相等@SuppressWarnings("unchecked")// 取得值V oldValue = (V) tab[i + 1];// 将value存入tab[i + 1] = value;// 返回旧值return oldValue;}}// 大小加1final int s = size + 1;// Use optimized form of 3 * s.// Next capacity is len, 2 * current capacity.// 如果3 * size大于length,则会进行扩容操作if (s + (s << 1) > len && resize(len))// 扩容后重新计算元素的值,寻找合适的位置进行存放continue retryAfterResize;// 结构性修改加1modCount++;// 存放key与valuetab[i] = k;tab[i + 1] = value;// 更新sizesize = s;return null;}}

View Code

  说明:若传入的key在表中已经存在了(强调:是同一个引用),则会用新值代替旧值并返回旧值;如果元素个数达到阈值,则扩容,然后再寻找合适的位置存放key和value。

  6. resize函数 

private boolean resize(int newCapacity) {// assert (newCapacity & -newCapacity) == newCapacity; // power of 2int newLength = newCapacity * 2;// 保存原来的tableObject[] oldTable = table;int oldLength = oldTable.length;// 旧表是否为最大容量的2倍if (oldLength == 2 * MAXIMUM_CAPACITY) { // can't expand any further// 之前元素个数为最大容量,抛出异常if (size == MAXIMUM_CAPACITY - 1)throw new IllegalStateException("Capacity exhausted.");return false;}// 旧表长度大于新表长度,返回falseif (oldLength >= newLength)return false;// 生成新表Object[] newTable = new Object[newLength];// 将旧表中的所有元素重新hash到新表中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;}}// 新表赋值给tabletable = newTable;return true;}

View Code

  说明:当表中元素达到阈值时,会进行扩容处理,扩容后会旧表中的元素重新hash到新表中。

  7. remove函数  

public V remove(Object key) {// 保证null的key会转化为Object(NULL_KEY)Object k = maskNull(key);Object[] tab = table;int len = tab.length;// 计算hash值int i = hash(k, len);while (true) {Object item = tab[i];// 找到key相等的项if (item == k) {modCount++;size--;@SuppressWarnings("unchecked")V oldValue = (V) tab[i + 1];tab[i + 1] = null;tab[i] = null;// 删除后需要进行后续处理,把之前由于冲突往后挪的元素移到前面来
                closeDeletion(i);return oldValue;}// 该项为空if (item == null)return null;// 下一项i = nextKeyIndex(i, len);}}

View Code

  8. closeDeletion函数 

private void closeDeletion(int d) {// Adapted from Knuth Section 6.4 Algorithm RObject[] tab = table;int len = tab.length;// Look for items to swap into newly vacated slot// starting at index immediately following deletion,// and continuing until a null slot is seen, indicating// the end of a run of possibly-colliding keys.
        Object item;// 把该元素后面符合移动规定的元素往前面移动for (int i = nextKeyIndex(d, len); (item = tab[i]) != null;i = nextKeyIndex(i, len) ) {// The following test triggers if the item at slot i (which// hashes to be at slot r) should take the spot vacated by d.// If so, we swap it in, and then continue with d now at the// newly vacated i.  This process will terminate when we hit// the null slot at the end of this run.// The test is messy because we are using a circular table.int r = hash(item, len);if ((i < r && (r <= d || d <= i)) || (r <= d && d <= i)) {tab[d] = item;tab[d + 1] = tab[i + 1];tab[i] = null;tab[i + 1] = null;d = i;}}}

View Code

  说明:在删除一个元素后会进行一次closeDeletion处理,重新分配元素的位置。

  下图表示在closeDeletion前和closeDeletion后的示意图

  

  说明:假设:其中,("aa" -> "aa")经过hash后在第0项,("bb" -> "bb")经过hash后也应该在0项,发生冲突,往后移到第2项,("cc" -> "cc")经过hash后在第2项,发生冲突,往后面移动到第4项,("gg" -> "gg")经过hash在第2项,发生冲突,往后移动到第6项,("dd" -> "dd")在第8项,("ee" -> "ee")在第12项。当删除("bb" -> "bb")后,进行处理后的元素布局如右图所示。

五、总结

  IdentityHashMap与HashMap在数据结构上很不相同,并且处理hash冲突的方法也不相同。其中,IdentityHashMap只有当key为同一个引用时才认为是相同的,而HashMap还包括equals相等,即内容相同。

【集合框架】JDK1.8源码分析之IdentityHashMap(四)相关推荐

  1. 【集合框架】JDK1.8源码分析之HashMap(一)

    转载自  [集合框架]JDK1.8源码分析之HashMap(一) 一.前言 在分析jdk1.8后的HashMap源码时,发现网上好多分析都是基于之前的jdk,而Java8的HashMap对之前做了较大 ...

  2. 【集合框架】JDK1.8源码分析HashSet LinkedHashSet(八)

    一.前言 分析完了List的两个主要类之后,我们来分析Set接口下的类,HashSet和LinkedHashSet,其实,在分析完HashMap与LinkedHashMap之后,再来分析HashSet ...

  3. 【JUC】JDK1.8源码分析之ArrayBlockingQueue(三)

    一.前言 在完成Map下的并发集合后,现在来分析ArrayBlockingQueue,ArrayBlockingQueue可以用作一个阻塞型队列,支持多任务并发操作,有了之前看源码的积累,再看Arra ...

  4. 图片加载框架Picasso - 源码分析

    简书:图片加载框架Picasso - 源码分析 前一篇文章讲了Picasso的详细用法,Picasso 是一个强大的图片加载缓存框架,一个非常优秀的开源库,学习一个优秀的开源库,,我们不仅仅是学习它的 ...

  5. JDK1.8源码分析:可重入锁ReentrantLock和Condition的实现原理

    synchronized的用法和实现原理 synchronized实现线程同步的用法和实现原理 不足 synchronized在线程同步的使用方面,优点是使用简单,可以自动加锁和解锁,但是也存在一些不 ...

  6. 集合之ArrayList(含JDK1.8源码分析)

    一.ArrayList的数据结构 ArrayList底层的数据结构就是数组,数组元素类型为Object类型,即可以存放所有类型数据.我们对ArrayList类的实例的所有的操作(增删改查等),其底层都 ...

  7. 【JUC】JDK1.8源码分析之ConcurrentHashMap

    一.前言 最近几天忙着做点别的东西,今天终于有时间分析源码了,看源码感觉很爽,并且发现ConcurrentHashMap在JDK1.8版本与之前的版本在并发控制上存在很大的差别,很有必要进行认真的分析 ...

  8. synchronousqueue场景_【JUC】JDK1.8源码分析之SynchronousQueue(九)

    一.前言 本篇是在分析Executors源码时,发现JUC集合框架中的一个重要类没有分析,SynchronousQueue,该类在线程池中的作用是非常明显的,所以很有必要单独拿出来分析一番,这对于之后 ...

  9. 01、JUL日志(JDK自带日志框架,包含源码分析)

    文章目录 前言 一.JUL架构介绍 1.1.认识不同组件 1.2.Logger 1.3.Handler 二.输出日志信息 三.自定义日志级别配置 3.1.认识Level类 3.2.输出不同等级日志 3 ...

最新文章

  1. SCI至上只是结果,而不是原因
  2. Ubuntu 设置Android adb 环境变量
  3. 记asp.net VB与C# 页面参数传值
  4. Tornado源码分析 --- 静态文件处理模块
  5. excel趋势线公式导出_Java 添加、读取、删除Excel中的图表趋势线
  6. 【2016年第4期】大数据时代的简约计算
  7. 动态规划(树形DP):HDU 5886 Tower Defence
  8. iOS 视频播放器旋转问题小结
  9. 字号与文字的大小关系
  10. python快速实现简易贪吃蛇小游戏
  11. 二、Excel大纲—基础篇
  12. Mapper method 'com.XXX.dao.XXXMapper.XXX' has an unsupported return type: class XXX
  13. php redis incr过期时间,Redis 利用 incr 和 expire 来限流, 并发导致过期时间失效问题...
  14. SD miniSD microSD TF CF MMC XD-Picture卡 SDIO CE-ATA SDHC SDXC
  15. 分布式任务调度系列 - PowerJob
  16. [转]给SSD(固态硬盘)编程
  17. Netscape中的keyCode和srcElement
  18. 《华为工作法》3 华为中的每个人既是工作者,也是管理者
  19. css和js优化_如何为更快的站点优化CSS和JS
  20. OpenGLES---点精灵

热门文章

  1. CentOS 中使用yum时常见的一种提示信息
  2. 架设nagios+rrdtool+pnp4nagios监控windows主机
  3. 使用Visual Studio重构与分析Python
  4. python3 上传文件到目标机器_再见Python 2.7,你好Python 3.7
  5. 5G NGC — 会话管理模型 — 基于 Flow 的 QoS 模型
  6. Service Mesh — Istio
  7. Go 语言编程 — 高级数据类型 — Map 集合
  8. C 家族程序设计语言发展史
  9. Python Module_Socket_网络编程
  10. Linux_RHEL7_YUM