JDK1.7版本中的HashMap

以下讲解基于JDK1.7

HashMap底层是一个数组,哈希值相同的元素放在数组中的相同的位置,多个相同哈希值的元素形成一个链表。也就是说,元素的组织形式是单向链表。

下面从put、get、remove这三个方法分析一下源代码,看看HashMap增删查改是怎么做的。

构造HashMap对象的时候做了初始化,指定默认的初始容量(数组长度)和增长因子

接下来,从put开始分析

从上面三段代码可以看出添加一个元素的基本流程:

(1)HashMap的key值允许为null,而且key为null的元素放在数组中下标为0的位置

(2)根据待插入元素的key值计算出一个哈希值,然后根据这个哈希值和数组的长度计算该元素将要放置的位置(PS:下标)。如果这个位置为空,那么直接插入。否则,遍历该位置上的链表,依次比较他们的key值是否相同,如果相同,则将用新值替换旧值,然后返回就值。

(3)正常插入,将待插入的元素放置在链表的头部,然后将其指向原先的链表头部(即:原先放置在待插入位置的元素)。也就是说,新插入的元素是放在头部的,我觉得这样做的好处可能是根据LRU的原则减少遍历的次数。

(4)有一种特殊情况是,扩容。

扩容就是,将原数组中的每个位置的元素都迁移到新数组中,在迁移到新数组的过程中同样先计算哈希值,然后得出将要放置在新数组中那个位置上。链表的迁移过程相当于将原先的链表倒置,先将头部的元素迁移过去,然后将下一个元素迁移过去,令next元素的next指向新数组位置上的元素,最终呈现出来的效果就是链表倒置。

接下来看get操作

get操作比较简单:

(1)根据key算哈希值,进而得出元素可能的位置,然后遍历该位置上的链表,比较key值是否相同,相同则返回,否则返回null

最后是remove

删除也相对比较简单:

(1)删除元素所在位置,遍历链表,比较key值,找到待删除元素以后,如果当前只有一个元素,直接删除,此位置置位NULL,否则将前面元素的next指向后面元素。

HashMap在并发情况下存在的问题(并发就是没法保证顺序)

(1)插入的元素可能被覆盖

假设有两个线程都执行到这里,线程1它的key=A,value=aaa,线程2它的key=B,value=bbb。

假设i=1,那么线程1执行的时候table[1]=new Entry<>(1234, "A", "aaa", null);

等到线程2执行的时候table[1]=new Entry<>(1234, "B", "bbb", null);

于是乎,线程1插入的数据就丢失了(或者说是被覆盖了)

(2)put的时候,链表可能形成环形数据结构,导致如果查找一个不存在的元素时死循环

那么环状是怎么形成的呢?发生在扩容的时候。请看图

假设有两个元素A和B,它们的关系是A.next=B,B.next=null

大概就是下面这个样子

假设有两个线程,线程-1和线程-2,它们在执行插入的时候都发现需要扩容,于是乎都开始扩容。

当然,扩容是在它们自己的内存中进行的。假设线程-1完成对A元素的迁移后准备对B进行迁移并执行到Entry<K,V> next = e.next;时还没执行时线程被挂起了。执行到线程-2先执行完扩容,于是扩容后的指向关系变成了这个样子:B.next=A,A.next=null

特别注意,看图上画的好像是元素直接放到数组的某个位置,但我们要知道,其它放的是元素的地址,也就是说元素本身的位置不变,修改的只是指针指向。尽管线程-2构造的新数组对线程-1而言是不可见的,但是不可否认,线程-2在扩容过程中已经将A和B的指向关系修改了,也就是说,此时,B是指向A的,这一点对线程-1而言是可见的。

接下来,线程-1醒来,继续执行

while(null != e) {

    Entry<,> next = e.;
    (rehash) {
        e.= == e.? : hash(e.);
    }
    i = (e., newCapacity);
    e.= newTable[i];
    newTable[i] = e;
    e = next;
}

此时,对照代码应该是这样的

经过这一遍,现在在新数组中的指向关系变成:B-->A-->NULL

紧接着,因为e已经是A了,所以null != e,于是再执行一遍

然后就变成这样了

接下来,麻烦来了。

查找C,经过计算C应该与A、B在数组的同一个位置,于是遍历链表

于是,通过A找到B,通过B又找到A,通过A又找到B,通过B又找到A,如此反复,永远都不为null,死循环

终于讲明白了

最后,再提一点,就是hash方法,字符串和非字符串算哈希值的方法是不一样的

参考:

http://blog.csdn.net/zhuqiuhui/article/details/51849692

https://www.cnblogs.com/binyue/p/3726403.html

时间: 2024-08-09 19:24:17

JDK1.7版本中的HashMap的相关文章

JDK1.8中对hashmap的优化

在Java编程语言中,最基本的结构就是两种,一个是数组,另外一个是模拟指针(引用),所有的数据结构都可以用这两个基本结构来构造的,HashMap也不例外.HashMap实际上是一个"链表散列"的数据结构,即数组和链表的结构,但是在jdk1.8里 加入了红黑树的实现,当链表的长度大于8时,转换为红黑树的结构. 从上图中可以看出,Java中HashMap采用了链地址法.链地址法,简单来说,就是数组加链表的结合.在每个数组元素上都一个链表结构,当数据被Hash后,得到数组下标,把数据放在对应

【1】Jdk1.8中的HashMap实现原理

HashMap概述 HashMap是基于哈希表的Map接口的非同步实现.此实现提供所有可选的映射操作,并允许使用null值和null键.此类不保证映射的顺序,特别是它不保证该顺序恒久不变. 内部实现 HashMap的数据结构(字段) 在Java编程语言中,最基本的结构就是两种,一个是数组,另外一个是模拟指针(引用),所有的数据结构都可以用这两个基本结构来构造的,HashMap也不例外.HashMap实际上是一个"链表散列"的数据结构,即数组和链表的结构,但是在jdk1.8里 加入了红黑

Jdk1.8中的HashMap实现原理

本文主要参考:美团点评技术团队 HashMap概述 HashMap是基于哈希表的Map接口的非同步实现.此实现提供所有可选的映射操作,并允许使用null值和null键.此类不保证映射的顺序,特别是它不保证该顺序恒久不变. HashMap的数据结构 在Java编程语言中,最基本的结构就是两种,一个是数组,另外一个是模拟指针(引用),所有的数据结构都可以用这两个基本结构来构造的,HashMap也不例外.HashMap实际上是一个"链表散列"的数据结构,即数组和链表的结构,但是在jdk1.8

JDK1.8中的HashMap.HashTable, ConcurrentHashMap有什么区别?

JDK1.8中的HashMap,HashTable,ConcurrentHashMap有什么区别? 答:HashMap是线程不安全的,底层采用数组+链表+红黑树的结构 HashTable是线程安全的,因为使用了Synchronized锁住了整个table,底层采用了数组+链表 ConcurrentHashMap是线程安全的,采用了CAS+同步锁Synchronized对链表头节点进行锁定,底层使用数组+链表+红黑树 HashMap的key和value可以是null,其他两个不行. 原文地址:ht

Java中的HashMap和HashTable

对于 Map ,最直观就是理解就是键值对,映射,key-value 形式.一个映射不能包含重复的键,一个键只能有一个值.平常我们使用的时候,最常用的无非就是 HashMap. HashMap 实现了 Map 接口,允许使用 null 值 和 null 键,并且不保证映射顺序. HashMap 有两个参数影响性能: 初始容量:表示哈希表在其容量自动增加之前可以达到多满的一种尺度加载因子:当哈希表中的条目超过了容量和加载因子的乘积的时候,就会进行重哈希操作.如下成员变量源码: 1 static fi

JAVA帮助文档全系列 JDK1.5 JDK1.6 JDK1.7 官方中英完整版下载

JAVA帮助文档全系列 JDK1.5 JDK1.6 JDK1.7 官方中英完整版下载JDK(Java Development Kit,Java开发包,Java开发工具)是一个写Java的applet和应用程序的程序开发环境.它由一个处于操作系统层之上的运行环境还有开发者编译,调试和运行用Java语言写的applet和应用程序所需的工具组成. JDK(Java Development Kit)是Sun Microsystems针对Java开发员的产品.自从Java推出以来,JDK已经成为使用最广泛

关于Java中的HashMap的深浅拷贝的测试与几点思考

0.前言 工作忙起来后,许久不看算法,竟然DFA敏感词算法都要看好一阵才能理解...真是和三阶魔方还原手法一样,田园将芜,非常可惜啊. 在DFA算法中,第一步是需要理解它的数据结构,在此基础上,涉及到一些Hashmap的赋值.这里的赋值非常有趣,三个Hashmap翻来覆去赋值,就解决了敏感词表的初始化. 里面都是属于下文中的Hashmap"浅拷贝",那么究竟Java中的Hashmap有哪些拷贝方法呢? 1.测试代码 HashMap hm_source = new HashMap();

警告:XXXXX 是Sun的专用API,可能会在未来版本中删除

今天遇到一个很dan疼的问题,maven编译项目时:警告:XXXXX 是Sun的专用API,可能会在未来版本中删除,以前也遇到过,换了个jdk版本就可以了,今天试了好几个版本都不好使,网上有也有很多说这个问题的,下面简单说一下,当做记录,有什么不对的地方,多多指教!!! 造成这个错误的原因是maven-compiler-plugin 2.3.2的问题,需要将这个插件升级: <plugin> <groupId>org.apache.maven.plugins</groupId&

深入剖析 Java7 中的 HashMap 和 ConcurrentHashMap

本文将深入剖析 Java7 中的 HashMap 和 ConcurrentHashMap 的源码,解析 HashMap 线程不安全的原理以及解决方案,最后以测试用例加以验证. 1 Java7 HashMap HashMap 的数据结构: 从上图中可以看出,HashMap 底层就是一个数组结构,数组中的每一项又是一个链表. 通过查看 JDK 中的 HashMap 源码,可以看到其构造函数有一行代码: public HashMap(int initialCapacity, float loadFac