主要记录hashMap的一些基本操作源码实现原理以及多线程情况下get()操作的死循环引发原因

一、hashMap简介

1.hashMap集合的主要属性及方法

(默认初始化容量)DEFAULT_INITIAL_CAPACITY = 16

(默认最大容量)MAXIMUM_CAPACITY = 1 << 30

(默认加载因子)DEFAULT_LOAD_FACTOR = 0.75f

(Entry数组)Entry[] table

(Entry实例的数量)size

put(K key, V value)方法

get(K key)方法

2.hashMap结构及操作(new方法 put方法 get方法):

数组+链表的形式,以实例Entry<K,V>的形式存储

a.new方法

从图中我们可以看到一个hashmap就是一个数组结构,当新建一个hashmap的时候,就会初始化一个数组(默认长度16,加载因子0.75):

源码:

    /*** Constructs an empty <tt>HashMap</tt> with the default initial capacity* (16) and the default load factor (0.75).*/public HashMap() {this.loadFactor = DEFAULT_LOAD_FACTOR;threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);table = new Entry[DEFAULT_INITIAL_CAPACITY];init();}

例:table[0],table[1],table[2],table[3]...table[15]

b.put方法

当执行put方法时,会先计算key值的hash值,将hash值和(数组长度减一得到的值)进行与运算,得到数组下标值,将键值对以Entry实例的形式放入数组中:

put方法源码:

    /*** Associates the specified value with the specified key in this map.* If the map previously contained a mapping for the key, the old* value is replaced.** @param key key with which the specified value is to be associated* @param value value to be associated with the specified key* @return the previous value associated with <tt>key</tt>, or*         <tt>null</tt> if there was no mapping for <tt>key</tt>.*         (A <tt>null</tt> return can also indicate that the map*         previously associated <tt>null</tt> with <tt>key</tt>.)*/public V put(K key, V value) {if (key == null)return putForNullKey(value);int hash = hash(key.hashCode()); //计算key的hash值int i = indexFor(hash, table.length); //根据hash值和数组长度计算数组位置for (Entry<K,V> e = table[i]; e != null; e = e.next) {
//如遇到hash冲突(e不为空),遍历链表
            Object k;if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { //如遇到key值相等的,进行替换V oldValue = e.value;e.value = value;e.recordAccess(this);return oldValue;}}

indexFor方法源码(计算数组下标),方法和简单,就把key值的hash值和数组长度-1做了与运算
    /*** Returns index for hash code h.*/static int indexFor(int h, int length) {return h & (length-1);}

例:添加键值对为7,77,调用方法put(7,77),7经过计算后的hash值为7(可自行验证,不多做解释),调用indexFor方法进行与运算:0111&1111=0111,下标值为7。

table[0],table[1],table[2],table[3]...table[7]=7...table[15]

再添加元素8,put(8,88),15经过计算后的hash值仍为8,1000&1111=1000,下标值为8。

table[0],table[1],table[2],table[3]...table[7]=7,table[8]=8...table[15]

再添加元素22,put(22,2222),22经过计算后的hash值为23,10111&1111=7,下标值为7,此时由于table[7]中已存在元素(7,77),产生hash冲突,hashMap会将23放入table[7]中,再执行(e=e.next),以链表形式将next指针指向之前的元素(7,77),如下图所示:

当Entry的个数超过最大容量值*负载因子(16*0.75=12)时,hashMap会进入resize方法,重新创建一个数组并扩容为原来的2倍,再将数据拷贝到新的数组中,如下图所示:

resize方法源码:

    /*** Rehashes the contents of this map into a new array with a* larger capacity.  This method is called automatically when the* number of keys in this map reaches its threshold.** If current capacity is MAXIMUM_CAPACITY, this method does not* resize the map, but sets threshold to Integer.MAX_VALUE.* This has the effect of preventing future calls.** @param newCapacity the new capacity, MUST be a power of two;*        must be greater than current capacity unless current*        capacity is MAXIMUM_CAPACITY (in which case value*        is irrelevant).*/void resize(int newCapacity) {Entry[] oldTable = table;int oldCapacity = oldTable.length;if (oldCapacity == MAXIMUM_CAPACITY) {threshold = Integer.MAX_VALUE;return;}Entry[] newTable = new Entry[newCapacity];transfer(newTable);table = newTable;threshold = (int)(newCapacity * loadFactor);}

c.get方法:

当执行get方法时,根据key值的hash值,得到数组下标,将数组内的entry的key值与get中的参数做对比(如该数组内有链表,则会继续遍历链表),若hash相等且equals,则返回。

get方法源码:

   /*** Returns the value to which the specified key is mapped,* or {@code null} if this map contains no mapping for the key.** <p>More formally, if this map contains a mapping from a key* {@code k} to a value {@code v} such that {@code (key==null ? k==null :* key.equals(k))}, then this method returns {@code v}; otherwise* it returns {@code null}.  (There can be at most one such mapping.)** <p>A return value of {@code null} does not <i>necessarily</i>* indicate that the map contains no mapping for the key; it's also* possible that the map explicitly maps the key to {@code null}.* The {@link #containsKey containsKey} operation may be used to* distinguish these two cases.** @see #put(Object, Object)*/public V get(Object key) {if (key == null)return getForNullKey();int hash = hash(key.hashCode()); //计算hash值for (Entry<K,V> e = table[indexFor(hash, table.length)];e != null;e = e.next) {
//这里遍历数组中的链表,若找到key值相等的,则返回
            Object k;if (e.hash == hash && ((k = e.key) == key || key.equals(k)))return e.value;}return null;}


 二、Java多线程下的HashMap死循环

摘自:http://blog.csdn.net/xiaohui127/article/details/11928865

正常的ReHash的过程

画了个图做了个演示。

  • 我假设了我们的hash算法就是简单的用key mod 一下表的大小(也就是数组的长度)。
  • 最上面的是old hash 表,其中的Hash表的size=2, 所以key = 3, 7, 5,在mod 2以后都冲突在table[1]这里了。
  • 接下来的三个步骤是Hash表 resize成4,然后所有的<key,value> 重新rehash的过程

并发下的Rehash

1)假设我们有两个线程。我用红色和浅蓝色标注了一下。

我们再回头看一下我们的 transfer(resize方法中)代码中的这个细节:

                do {Entry<K,V> next = e.next; // <--假设线程一执行到这里就被调度挂起了  int i = indexFor(e.hash, newCapacity);e.next = newTable[i];newTable[i] = e;e = next;} while (e != null);

而我们的线程二执行完成了。于是我们有下面的这个样子。

注意,因为Thread1的 e 指向了key(3),而next指向了key(7),其在线程二rehash后,指向了线程二重组后的链表。我们可以看到链表的顺序被反转后。

2)线程一被调度回来执行。

  • 先是执行 newTalbe[i] = e;
  • 然后是e = next,导致了e指向了key(7),
  • 而下一次循环的next = e.next导致了next指向了key(3)

3)一切安好。

线程一接着工作。把key(7)摘下来,放到newTable[i]的第一个,然后把e和next往下移

4)环形链接出现。

e.next = newTable[i] 导致  key(3).next 指向了 key(7)

注意:此时的key(7).next 已经指向了key(3), 环形链表就这样出现了。

于是,当我们的线程一调用到,HashTable.get(11)时,悲剧就出现了——Infinite Loop。

转载于:https://www.cnblogs.com/nsxqf/p/6580982.html

HashMap简单源码及多线程下的死循环相关推荐

  1. vfp赋值超过7位出错_JDK1.7下的HashMap的源码分析

    源码分析jdk1.7下的HashMap 我们都知道1.7版本的hashmap的底层是数组加链表构成的,那么今天我们就来自己分析一波源码~ 篇幅有点长,废话不多说,直接开始分析~ 「属性声明」 //初始 ...

  2. HashMap的源码分析(不作标题党)

    HashMap是我们经常用的数据结构,采用了key-value的方式来存储数据.在JDK 1.8的版本中也是对其做了优化修改,现在我们来通过在JDK 1.8的环境下的源代码分析一下HashMap的工作 ...

  3. hashmap的特性?HashMap底层源码,数据结构?Hashmap和hashtable ConcurrentHashMap区别?

    1.hashmap的特性? 允许空键和空值(但空键只有一个,且放在第一位) 元素是无序的,而且顺序会不定时改变 key 用 Set 存放,所以想做到 key 不允许重复,key 对应的类需要重写 ha ...

  4. 浅谈对HashMap的理解,以及对HashMap部分源码的分析

    文章目录 一.什么是HashMap 1.1 Hash是什么 1.2 Map是什么 Map的特点 Map和Hash的结合 二.HashMap部分源码理解 2.1 关键变量 2.2 关键逻辑 2.3 关键 ...

  5. HashMap 底层源码细致分析

    JDK集合HashMap 底层源码细致分析 前言 提示:对于初始 HashMap 的小伙伴来说,不推荐直接硬啃,建议先看一下如下几个视频教程之后再回头好好理解.(一遍看不懂则反复看,一小块一小块的找对 ...

  6. Java基础笔记(2)——HashMap的源码,实现原理,底层结构是怎么样的

    Java基础笔记(2)--HashMap的源码,实现原理,底层结构是怎么样的 HashMap的源码,实现原理,底层结构 1.HashMap: HashMap是基于哈希表的 Map 接口的实现.此实现提 ...

  7. 面试官系统精讲Java源码及大厂真题 - 31 AbstractQueuedSynchronizer 源码解析(下)

    31 AbstractQueuedSynchronizer 源码解析(下) 低头要有勇气,抬头要有底气. 引导语 AQS 的内容太多,所以我们分成了两个章节,没有看过 AQS 上半章节的同学可以回首看 ...

  8. 量化交易中VWAP/TWAP算法的基本原理和简单源码实现(C++和python)(转)

    量化交易中VWAP/TWAP算法的基本原理和简单源码实现(C++和python) 原文地址:http://blog.csdn.net/u012234115/article/details/728300 ...

  9. 终于搞懂HashMap的源码了!!!

    背景 HashMap是我们在平时开发最常用的容器之一,但是我们有真正了解过他吗?他是线程安全的吗?他是以何种方式来存储的呢?为什么初始化的容器大小时2的n次幂呢?他是如何进行扩容的呢?他是如何实现并发 ...

  10. HashSet源码解析(最好先看HashMap的源码解析)

    HashMap的源码解析:https://mp.csdn.net/console/editor/html/106188425 HashSet:Java中的一个集合类,该容器不允许包含重复的数值 pub ...

最新文章

  1. android闪退日志收集
  2. Shiro+springboot+mybatis+EhCache(md5+salt+散列)认证与授权-03
  3. linux管理员常用的命令分享
  4. 网页设计上机考试原题_《网页设计》上机考试试题
  5. AI芯片格局最全分析
  6. hadoop--日志聚集功能的配置
  7. hdu5354 Bipartite Graph
  8. swoole php 微信推送,基于thinkphp5、swoole和easywechat微信模板消息推送
  9. java股票公式源码_各种涨停公式源码
  10. 计算机专业英语词汇分类收录
  11. Android开发打开手机自带浏览器
  12. win7修改网络计算机名字,Win7系统中不能修改计算机名字是怎么回事?
  13. SQLmap Tamper编写方法(笔记)
  14. ef连接mysql报root没有权限_EF下使用自定义的connectionString避免数据库密码泄露
  15. Realtek WiFi定频工具使用操作指南(rtl8188au/rtl8812au/rtl8192cu)
  16. HTML5期末大作业:家居/家电/家居网站设计——html家具装饰网站设计30页(含论文) HTML+CSS+JavaScript 学生DW网页设计作业成品 web课程设计网页规划与设计 计算机...
  17. 管理信息系统【一】之 管理信息系统概论
  18. react 使用 Youtube 播放器
  19. django的admin速度慢优化:date_hierarchy、list_filter
  20. linux mp3 乱码,linux下mp3乱码终极解决方案

热门文章

  1. Windows系统过滤病毒功能吗
  2. 8.9 元学习网络结构讲解
  3. 5.3傅立叶变换意境级讲解
  4. 无法添加外键约束的原因(cannot add foreign key constraint)
  5. Spark:Spark 编程模型及快速入门
  6. 【机器学习笔记】使用lightgbm画并保存Feature Importance
  7. python里面两个大于号_听说92.8%的人答不对这道Python题,我不信,后来我信了!真有趣...
  8. 主角有智能芯片的种田小说_5本搞笑玩梗的良品小说,文风轻松幽默,一本正经地逗你笑...
  9. JavaWeb—作业【建立新闻数据库以及插入数据】
  10. D - Send a Table (UVA - 10820)