前几天工作忙得焦头烂额时,同事问了一下关于Map的特性,刹那间懵了一下,紧接着就想起来了一些关于Map的一些知识,因为只要涉及到Collection集合类时,就会谈及Map类,因此理解好Map相关的知识是灰常重要的。

  Collection集合类 与 Map类的层次结构,如下图:

  

  Map是用来存储 Key-Value(K-V)键值对,且不允许Key重复。(JDK 1.8)

  Map的子类包含Hashtable,HashMap,LinkedHashMap,TreeMap,EnumMap,J.U.C并发包下面包含ConcurrentHashMap,ConcurrentSkipListMap。

  注:Hashtable是通过“synchronized”来实现同步,ConcurrentHashMap是通过“分段锁”来实现并发,ConcurrentSkipListMap是通过“跳表”来实现并发。

  数据结构对比

  JDK 1.7及之前,HashMap的底层是数组和链表结合使用,可以说其结构是单向链表构成的数组,即链表散列,又称拉链法,即数组中的每一个序列空间代表一链表,若在插入新元素时,如果哈希冲突,则将新的元素插入到冲突的序列空间的链表头部。 

  

   JDK 1.8之后,HashMap的底层是数组链表和二叉树结合使用,其结构是先以链表结构进行存储,如果当链表的节点数(阈值)大于等于8时,再将其转换为二叉树结构,若该二叉树结构的节点数大于64,则再次resize,触发rehash操作,重新分布节点

   

JDK 1.7 | 1.8    HashMap 方法分析

  

  1. hashmap在JDK1.7|1.8中hash()方法优化:此处参考博客链接_hashmap冲突的解决方法以及原理分析

    其实关于hash优化以及为什么是2的次幂,能理解,但是总归没有辣么详细,所以就参考了其他博主的博客,如果不合适,还请提出,会进行删除的。

     

  2. hashmap在JDK中resize()方法

当HashMap的长度超过临界值时(默认16*0.75=12),就会进行扩容。

      通过调用resize()方法重新创建一个原HashMap大小的两倍的元素数组newTable,最大扩容到 MAXIMUM_CAPACITY = 1 << 30,并将原先table的元素全部移到newTable里面,重新计算hash,然后再重新根据hash分配位置。这个过程叫作rehash(最消耗性能的操作),因为它重新调用hash方法找到新的bucket位置。所以创建HashMap的时候,如果能预知元素个数,应尽量指定初始容量,这样可以提高HashMap的性能,避免重复resize()、rehash造成的性能损耗。

     若在多线程情况下,多个线程同时出发resize()方法,会造成死循环操作

     

    通过上面的解析,可以得到使用的是2次幂的扩展(指长度扩为原来2倍),所以,元素的位置要么是在原位置,要么是在原位置再移动2次幂的位置。
    因此,我们在扩充HashMap的时候,不需要像JDK1.7的实现那样重新计算hash,通过使用(e.hash & oldCap)来计算高位和低位的hash值,来把原来在一个槽位上面的链表拆分成两个链表即可。
    有一点注意区别,JDK1.7中rehash的时候,旧链表迁移新链表的时候,如果在新表的数组索引位置相同,则链表元素会倒置,但是从上图可以看出,JDK1.8不会倒置。

  3. hashmap线程不安全,效率高

          HashMap线程是不安全的,如果在多线程环境下,会出现未知异常(谨记)。

   如果要在多线程情况下使用Map,有以下几种可供选择:

    ①、使用Hashtable(类中方法全是synchronized,效率特低,没有过这种);

    ②、使用Collections.synchronizedMap(new HashMap(...));实现,但不建议使用,使用迭代器遍历的时候修改映射结构容易出错;

    ③、使用guava中ImmutableMap,它是线程安全的,且方便快捷的组装键值对(建议);

        ImmutableMap<String, Integer> map1 = ImmutableMap.<String, Integer>builder().put("A", 1) .put("B"2) .put("C", 3) .build();

        ImmutableMap<String, Integer> map2 = ImmutableMap .<String, String>of("X", 0);

    ④、使用java.util.concurrent.ConcurrentHashMap,采用分段锁(JDK1.7) / CAS(JDK1.8)、相对安全,效率高(建议);

  4. hashmap其他方法解析

   其他方法后续有时间在进行补充...

     JDK 中 HashMap 的其他问题

  暂未想到,如有错误,还请指正 ...

HashMap源码详解与对比的更多相关文章

  1. Java HashMap源码详解

    Java数据结构-HashMap 目录 Java数据结构-HashMap 1. HashMap 1.1 HashMap介绍 1.1.1 HashMap介绍 1.1.2 HashMap继承图 1.2 H ...

  2. HashMap源码详解(JDK7版本)

    一.内部属性 内部属性源码: //内部数组的默认初始容量,作为hashmap的初始容量,是2的4次方,2的n次方的作用是减少hash冲突 static final int DEFAULT_INITIA ...

  3. Shiro 登录认证源码详解

    Shiro 登录认证源码详解 Apache Shiro 是一个强大且灵活的 Java 开源安全框架,拥有登录认证.授权管理.企业级会话管理和加密等功能,相比 Spring Security 来说要更加 ...

  4. Activiti架构分析及源码详解

    目录 Activiti架构分析及源码详解 引言 一.Activiti设计解析-架构&领域模型 1.1 架构 1.2 领域模型 二.Activiti设计解析-PVM执行树 2.1 核心理念 2. ...

  5. 源码详解系列(七) ------ 全面讲解logback的使用和源码

    什么是logback logback 用于日志记录,可以将日志输出到控制台.文件.数据库和邮件等,相比其它所有的日志系统,logback 更快并且更小,包含了许多独特并且有用的特性. logback ...

  6. Mybatis源码详解系列(四)--你不知道的Mybatis用法和细节

    简介 这是 Mybatis 系列博客的第四篇,我本来打算详细讲解 mybatis 的配置.映射器.动态 sql 等,但Mybatis官方中文文档对这部分内容的介绍已经足够详细了,有需要的可以直接参考. ...

  7. vue 源码详解(二): 组件生命周期初始化、事件系统初始化

    vue 源码详解(二): 组件生命周期初始化.事件系统初始化 上一篇文章 生成 Vue 实例前的准备工作 讲解了实例化前的准备工作, 接下来我们继续看, 我们调用 new Vue() 的时候, 其内部 ...

  8. RocketMQ源码详解 | Producer篇 · 其二:消息组成、发送链路

    概述 在上一节 RocketMQ源码详解 | Producer篇 · 其一:Start,然后 Send 一条消息 中,我们了解了 Producer 在发送消息的流程.这次我们再来具体下看消息的构成与其 ...

  9. RocketMQ源码详解 | Broker篇 · 其一:线程模型与接收链路

    概述 在上一节 RocketMQ源码详解 | Producer篇 · 其二:消息组成.发送链路 中,我们终于将消息发送出了 Producer,在短暂的 tcp 握手后,很快它就会进入目的 Broker ...

随机推荐

  1. Xml Helper

    类的完整代码: using System;using System.Collections;using System.Xml; namespace Keleyi.Com.XmlDAL{public c ...

  2. IIS反向代理

  3. springcloud系列八 整合Hystrix

    feign本身是支持Hystrix的,所以不需要引入其他依赖: 我们可以看看feign这个项目的依赖,就是引入这个依赖的pom.xml 要想看这个很简单,点击那个依赖进去就可以了 点进去就可以看到 & ...

  4. poj1094 拓扑排序(出度入度简单使用)

    Sorting It All Out Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 37764   Accepted: 13 ...

  5. 毕业设计 python opencv实现车牌识别 预处理

    主要代码参考https://blog.csdn.net/wzh191920/article/details/79589506 GitHub:https://github.com/yinghualuow ...

  6. 测试转型之路--学习ing

    http://www.importnew.com/27309.html 测试开发工程师 - 抖音方向 职位描述1.深度参与产品研发项目, 协同产品经理.业务研发.用户反馈团队优质交付产品:2.参与质量 ...

  7. classloader 学习

    classloader就是把类文件加载到jvm中供虚拟机使用,先看一个magic小例子: 首先,我定义一个alex/vicky包,然后在这个包内定义一个接口: public interfaceISer ...

  8. No bean named 'xxxxxxx' available--springboot 上线打war包

    springboot项目发布上线后,报错:No bean named 'xxxxxxx' available 因为我开发时pom用的jar,但上线发布war.解决方法: 1.pom.xml <p ...

  9. sscanf()分割字符数组

    sscanf与scanf类似,都是用于输入的,只是后者以键盘(stdin)为输入源,前者以固定字符串为输入源. 头文件: #include<stdio.h> 或者 #include < ...

  10. linux 网卡配置文件详解2018-03-07

    转自:https://www.cnblogs.com/ienino/p/7717092.html 配置文件位置:/etc/sysconfig/network-scripts/ifcfg-eth0 1. ...