回顾HashMap
一、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的更多相关文章
- 阅读源码,HashMap回顾
目录 回顾 HashMap简介 类签名 常量 变量 构造方法 tableSizeFor方法 添加元素 putVal方法 获取元素 getNode方法 总结 本文一是总结前面两种集合,补充一些遗漏,再者 ...
- HashMap探究
HashMap 前置 //初始化容量 static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; //容器最大容量 static final i ...
- HashMap的扩容机制以及默认大小为何是2次幂
HashMap的Put方法 回顾HashMap的put(Key k, Value v)过程: (1)对 Key求Hash值,对n-1取模计算出Hash表数组下标 (2)如果没有碰撞,直接放入桶中,即H ...
- 关于JDK1.8 HashMap扩容部分源码分析
今天回顾hashmap源码的时候发现一个很有意思的地方,那就是jdk1.8在hashmap扩容上面的优化. 首先大家可能都知道,1.8比1.7多出了一个红黑树化的操作,当然在扩容的时候也要对红黑树进行 ...
- HashMap之tableSizeFor方法图解
目录 普通人的简单粗暴方式 示例代码 问题 大神的实现 移位的思想 全过程示意图 初始值 右移一位+或运算 右移二位+或运算 右移四位+或运算 右移八位+或运算 右移十六位+或运算 结果+1 初始容量 ...
- Java面试问题汇总
转一些面试经验 刚看到下面这份面试清单,从个人的开发面试经历看,里面总结的大部分内容还是很不错的.年后想跳槽的朋友可以选取里面的问题准备一下. GitHub上的面试总结帖 Interview-Note ...
- 【大数据】Scala学习笔记
第 1 章 scala的概述1 1.1 学习sdala的原因 1 1.2 Scala语言诞生小故事 1 1.3 Scala 和 Java 以及 jvm 的关系分析图 2 1.4 Scala语言的特点 ...
- 大数据技术之_16_Scala学习_07_数据结构(上)-集合
第十章 数据结构(上)-集合10.1 数据结构特点10.1.1 Scala 集合基本介绍10.1.2 可变集合和不可变集合举例10.2 Scala 不可变集合继承层次一览图10.2.1 图10.2.2 ...
- java 面试2019
[第一部分] 面试要领[第1题] 流程必知必会[第2题] JDK源码[第二部分] 类和对象[第二篇] 面向对象基础[第1题] 面向对象是什么?[第2题] 类加载的过程[第3题] 类加载器有哪些[第4题 ...
随机推荐
- async await与promise
1.async 的返回值一定是一个promise.,即使你的函数里没有return. // 测试async的返回值 async function testAsync() { } let result ...
- 温顾知新系列-JAVA网络编程系统(1)- 流
一.字节流 1. 输出流 Java的基本输出流类是java.io.OutputStream; public abstract calss OutputStream 此类常用的写入数据的基本方法如下: ...
- Random库 --Python3
随机数是随机试验的结果,是计算机通过随即种子根据一定算法计算出来的,随机种子通常可以由系统时钟产生.下面是random库中基本方法: 1.random():产生一个0到1之间的随机浮点数:0<= ...
- Petrozavodsk Winter Camp, Andrew, 2014, Bipartite Bicolored Graphs
由i个点和j个点组成的二分图个数为 $3^{ij}$,减去不联通的部分得到得到由i,j个点组成的联通二分图个数 $g_{i,j} = 3_{ij} - \sum_{k=1}^i \sum_{l=0}^ ...
- pymysql连接数据库,读取表内容
python中有MySQLdb.pymysql等数据库模块,本文用pymysql模块连接mysql数据库,并且读取数据库表 看过其他博文的介绍,把程序和数据库比作两个目的地,将游标比喻成运输货车 很是 ...
- (02) 第一个springboot程序
1. 创建一个springboot程序 1. idea 自带的springboot插件 2. 直接从https://start.spring.io 创建好程序下载下来, 之后覆盖你的创建的项目 2. ...
- Java入门自学笔记
一.变量 变量需要一个名字,变量的名字是一种“标识符”,意思是它是用来识别这个和那个的不同的名字. 标识符的构造规则:只能有字母.数字和下划线组成,数字不能在首位,java的关键字(保留字)不可以用做 ...
- java使用StringBuilder的方法反转字符串输出
public class FanZhuan { public static void main(String[] args) { /**第一种方法*/ String s = "9876543 ...
- 错误:Bean property 'sessionFactory' is not writable or has an invalid setter method.
Caused by: org.springframework.beans.NotWritablePropertyException: Invalid property 'sessionFactory' ...
- nopcommerce 4.1 学习2 -插件之挂件
先了解下nop4.1的一些插件有哪些类型: 1.支付插件 Nop.Plugin.Payments.PayPalStandard Nop.Plugin.Payments.CheckMoneyOrd ...