一、HashMap的原理简述

HashMap是基于哈希表的非线程安全的Map实现,内部采用数组+链表实现,其内部类Node定义了数据元素类型,它扩展了Map.Entry<K,V>增加了指向下一个node结点的指针next。当向Map中加入一个键值对时,先根据键的haseCode计算hash值(为了尽量避免hash冲突,让键值对构成的node结点均匀的分布在数组上),然后算出它在数组中下标值,若此处为null则直接插入,否则比较此处node对象的hash值,若相同则修改,若不同则将要插入的node对象的next指针指向此处的node对象,然后将要插入的node对象放到该位置。

HashMap的性能影响最大的参数就是容量和负载以及hash算法的选择。如果HashMap中元素数超过容量*负载的值时就会产生扩容,为尽量减少扩容造成的性能消耗,在使用HashMap时尽量指定初始容量大小,而实际构造HashMap的初始容量大小会设定为不必指定容量小的2的n次方,以方便使用&运算来代替取模操作提升程序性能。

如果HashMap中元素的键计算的hash值冲突严重,会导致此hash值对应的链表过长,从而影响HashMap的读取性能,因此在JDK8中采用数组+链表+红黑树的方式来优化HashMap,默认当链表长度大于8时就采用红黑树来实现。

HashMap不是线程安全的,JDK采用Fail-Fast机制进行检测错误,当程序在遍历HashMap元素时如果检测到有其它线程修改了HashMap对象,就抛出并发修改异常。其内部实现是用modCount记录HashMap对象的修改次数,每当进行添加、删除、修改元素等修改操作时modCount++,遍历时检测到这个值前后不同就认为被其它线程修改过。Fail-Fast机制是错误检测机制,不一定能得到保证,如果要在多线程中使用,程序必须要在外部做同步处理,比如使用concurrent包下的类。

HashMap是遍历顺序是不确定的,若想要按确定顺序访问,可采用LinkedHashMap,它内部维护了一个双重链表,定义了迭代顺序,这个迭代顺序可以是插入顺序或者是访问顺序。默认是插入顺序,可通过构造函数指定按访问顺序排,可以方便的实现一个LRU缓存。

HashMap元素时无需的,若想要可排序的map,可以采用TreeMap。

二、HashSet的原理

HashSet基于HashMap实现,内部使用HashMap来保存元素。对于HashSet中保存的对象,要重写其equals和hashCode方法,以保证放入对象的唯一性。

三、ConcurrentHashMap原理

ConcurrentHashMap是线程安全的HashMap,它牺牲了遍历get操作的强一致性,通过弱一致性换来了多线程并发的高性能,可以用来代替HashTable或Collections.synchronizedMap(hashMap),因为它们实现同步的方式都是对整个Hash表结构做锁定操作,在锁表过程,其它线程都要等待,这在元素多时性能很低。在JDK1.7中采用分段锁技术来控制并发,内部先是Segment[]成员,它的每个元素时HashEntry[],它里面的每个元素是键值对,因此它在理想情况下可并发访问的线程数是segment数组的大小。它把HashMap分割成若干个Segment,在put的时候需要锁住Segment,get时候不加锁,使用volatile来保证可见性,当要统计全局时(比如size),首先会尝试多次计算modcount来确定,这几次尝试中,是否有其他线程进行了修改操作,如果没有,则直接返回size。如果有,则需要依次锁住所有的Segment来计算。

jdk7中ConcurrentHashmap中,当长度过长碰撞会很频繁,链表的增改删查操作都会消耗很长的时间,影响性能,因此在JDK1.8中采用了进一步的优化,从原来的ReentrantLock+Segment+HashEntry变为synchronized+CAS+Node数组+链表+红黑树来实现,将锁的粒度从Segment降低到首个HashEntry节点,设计了MOVED状态 当resize的中过程中其它线程要put数据时会能帮助resize,使用3个CAS操作来确保node的一些操作的原子性,代替了锁,使用sizeCtl的不同值来代表不同含义,起到了控制的作用。

注意:ConcurrentHashMap再设计实现时要求传入Key和Value都不能为null(为了分辨volatile变量是否是未赋值状态,从而方便多线程并发判断),HashTable中的Key、Value也不能为null。TreeMap不允许Key为null值,HashMap的Key、Value都可以为null值。

ConcurrentHashMap原理分析(1.7与1.8)

https://www.cnblogs.com/wxd0108/p/6382196.html

http://www.importnew.com/26049.html

 

回顾HashMap的更多相关文章

  1. 阅读源码,HashMap回顾

    目录 回顾 HashMap简介 类签名 常量 变量 构造方法 tableSizeFor方法 添加元素 putVal方法 获取元素 getNode方法 总结 本文一是总结前面两种集合,补充一些遗漏,再者 ...

  2. HashMap探究

    HashMap 前置 //初始化容量 static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; //容器最大容量 static final i ...

  3. HashMap的扩容机制以及默认大小为何是2次幂

    HashMap的Put方法 回顾HashMap的put(Key k, Value v)过程: (1)对 Key求Hash值,对n-1取模计算出Hash表数组下标 (2)如果没有碰撞,直接放入桶中,即H ...

  4. 关于JDK1.8 HashMap扩容部分源码分析

    今天回顾hashmap源码的时候发现一个很有意思的地方,那就是jdk1.8在hashmap扩容上面的优化. 首先大家可能都知道,1.8比1.7多出了一个红黑树化的操作,当然在扩容的时候也要对红黑树进行 ...

  5. HashMap之tableSizeFor方法图解

    目录 普通人的简单粗暴方式 示例代码 问题 大神的实现 移位的思想 全过程示意图 初始值 右移一位+或运算 右移二位+或运算 右移四位+或运算 右移八位+或运算 右移十六位+或运算 结果+1 初始容量 ...

  6. Java面试问题汇总

    转一些面试经验 刚看到下面这份面试清单,从个人的开发面试经历看,里面总结的大部分内容还是很不错的.年后想跳槽的朋友可以选取里面的问题准备一下. GitHub上的面试总结帖 Interview-Note ...

  7. 【大数据】Scala学习笔记

    第 1 章 scala的概述1 1.1 学习sdala的原因 1 1.2 Scala语言诞生小故事 1 1.3 Scala 和 Java  以及 jvm 的关系分析图 2 1.4 Scala语言的特点 ...

  8. 大数据技术之_16_Scala学习_07_数据结构(上)-集合

    第十章 数据结构(上)-集合10.1 数据结构特点10.1.1 Scala 集合基本介绍10.1.2 可变集合和不可变集合举例10.2 Scala 不可变集合继承层次一览图10.2.1 图10.2.2 ...

  9. java 面试2019

    [第一部分] 面试要领[第1题] 流程必知必会[第2题] JDK源码[第二部分] 类和对象[第二篇] 面向对象基础[第1题] 面向对象是什么?[第2题] 类加载的过程[第3题] 类加载器有哪些[第4题 ...

随机推荐

  1. RN中API之NetInfo--浅谈

    我们在做移动端项目和手机APP应用时,避免不了要获取用户手机的网络状况.在使用RN技术开发APP时,其内置的NetInfo API就是为了解决这一问题的.下面简单的讲下NetInfo如何使用. 最新的 ...

  2. Maven的pom配置文件

    1.1           Maven的pom配置文件 1.1.1            pom文件内容和作用 Pom.xml文件用来设置项目的项目依赖.插件.项目版本等信息,其中必须的是xml的根元 ...

  3. 巧用同步请求处理react页面刷新问题

    很多时候,我们会遇到这种情况,组件加载需要请求后台数据,然后填充组件.那么我们一般会这样处理:如[使用异步请求的方式]代码: 加载组价的时候,未获得数据,render一个空的div: 然后异步请求数据 ...

  4. Asp.net core 学习笔记 (AutoMapper)

    参考 : http://www.cnblogs.com/xishuai/p/3700052.html http://www.cnblogs.com/xishuai/p/3704435.html htt ...

  5. pandas实现excel中的数据透视表和Vlookup函数功能

    在孩子王实习中做的一个小工作,方便整理数据. 目前这几行代码是实现了一个数据透视表和匹配的功能,但是将做好的结果写入了不同的excel中, 如何实现将结果连续保存到同一个Excel的同一个工作表中?还 ...

  6. js验证银行卡号 luhn校验规则

    <script type="text/javascript"> //luhn校验规则:16位银行卡号(19位通用): // 1.将未带校验位的 15(或18)位卡号从右 ...

  7. centos7安装doxygen

    编译 编译过程参考官网:https://www.stack.nl/~dimitri/doxygen/download.html 编译过程: git clone https://github.com/d ...

  8. vue教程自学笔记(二)

    三.模板语法 1.文本 数据绑定最常见的形式就是使用“Mustache”语法 (双大括号) 的文本插值. 通过v-once指令,你也能执行一次性地插值,当数据改变时,插值处的内容不会更新. 2.原始H ...

  9. python之路---面向对象编程(二)

    类的继承 1.在python3中,只有新式类,新式类的继承方式为:广度优先.而python2中,经典类的继承方式为:深度优先.那么我们来看看深度优先和广度优先的区别吧 如下图,为类之间的继承关系.B, ...

  10. python之路---面向对象编程(一)

    一.设计思想的发展 面向机器(代码复杂,效率低,学习成本高,开发周期长)-------->面向过程(扩展性差,不适用多变的需求改变)----------->面向对象(扩展性好,但是可控性差 ...