本文转载自 夏雪冬日:https://www.cnblogs.com/heyonggang/p/9112731.html

在实际面试过程中出现集合 Map 的概率接近 100%,可见不背上个 Map 相关的题目都不好意思去面试了。


如果你去面试,面试官不问你这个问题,你来找我^_^

下面直接来干货,先说这三个 Map 的区别:

1、HashTable

  • 底层数组+链表实现,无论key还是value都不能为null,线程安全,实现线程安全的方式是在修改数据时锁住整个HashTable,效率低,ConcurrentHashMap做了相关优化
  • 初始size为11,扩容:newsize = olesize*2+1
  • 计算index的方法:index = (hash & 0x7FFFFFFF) % tab.length

2、HashMap

  • 底层数组+链表实现,可以存储null键和null值,线程不安全
  • 初始size为16,扩容:newsize = oldsize*2,size一定为2的n次幂
  • 扩容针对整个Map,每次扩容时,原来数组中的元素依次重新计算存放位置,并重新插入
  • 插入元素后才判断该不该扩容,有可能无效扩容(插入后如果扩容,如果没有再次插入,就会产生无效扩容)
  • 当Map中元素总数超过Entry数组的75%,触发扩容操作,为了减少链表长度,元素分配更均匀
  • 计算index方法:index = hash & (tab.length – 1)

HashMap的初始值还要考虑加载因子:

  • 哈希冲突:若干Key的哈希值按数组大小取模后,如果落在同一个数组下标上,将组成一条Entry链,对Key的查找需要遍历Entry链上的每个元素执行equals()比较。
  • 加载因子:为了降低哈希冲突的概率,默认当HashMap中的键值对达到数组大小的75%时,即会触发扩容。因此,如果预估容量是100,即需要设定100/0.75=134的数组大小。
  • 空间换时间:如果希望加快Key查找的时间,还可以进一步降低加载因子,加大初始大小,以降低哈希冲突的概率。

HashMap和Hashtable都是用hash算法来决定其元素的存储,因此HashMap和Hashtable的hash表包含如下属性:

  • 容量(capacity):hash表中桶的数量
  • 初始化容量(initial capacity):创建hash表时桶的数量,HashMap允许在构造器中指定初始化容量
  • 尺寸(size):当前hash表中记录的数量
  • 负载因子(load factor):负载因子等于“size/capacity”。负载因子为0,表示空的hash表,0.5表示半满的散列表,依此类推。轻负载的散列表具有冲突少、适宜插入与查询的特点(但是使用Iterator迭代元素时比较慢)

除此之外,hash表里还有一个“负载极限”,“负载极限”是一个0~1的数值,“负载极限”决定了hash表的最大填满程度。当hash表中的负载因子达到指定的“负载极限”时,hash表会自动成倍地增加容量(桶的数量),并将原有的对象重新分配,放入新的桶内,这称为rehashing。

HashMap和Hashtable的构造器允许指定一个负载极限,HashMap和Hashtable默认的“负载极限”为0.75,这表明当该hash表的3/4已经被填满时,hash表会发生rehashing。

“负载极限”的默认值(0.75)是时间和空间成本上的一种折中:

  • 较高的“负载极限”可以降低hash表所占用的内存空间,但会增加查询数据的时间开销,而查询是最频繁的操作(HashMap的get()与put()方法都要用到查询)
  • 较低的“负载极限”会提高查询数据的性能,但会增加hash表所占用的内存开销
    程序猿可以根据实际情况来调整“负载极限”值。

3、ConcurrentHashMap

  • 底层采用分段的数组+链表实现,线程安全
  • 通过把整个Map分为N个Segment,可以提供相同的线程安全,但是效率提升N倍,默认提升16倍。(读操作不加锁,由于HashEntry的value变量是 volatile的,也能保证读取到最新的值。)
  • Hashtable的synchronized是针对整张Hash表的,即每次锁住整张表让线程独占,ConcurrentHashMap允许多个修改操作并发进行,其关键在于使用了锁分离技术
  • 有些方法需要跨段,比如size()和containsValue(),它们可能需要锁定整个表而而不仅仅是某个段,这需要按顺序锁定所有段,操作完毕后,又按顺序释放所有段的锁
  • 扩容:段内扩容(段内元素超过该段对应Entry数组长度的75%触发扩容,不会对整个Map进行扩容),插入前检测需不需要扩容,有效避免无效扩容

Hashtable和HashMap都实现了Map接口,但是Hashtable的实现是基于Dictionary抽象类的。Java5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。

HashMap基于哈希思想,实现对数据的读写。当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算hashcode,然后找到bucket位置来存储值对象。当获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象。HashMap使用链表来解决碰撞问题,当发生碰撞时,对象将会储存在链表的下一个节点中。HashMap在每个链表节点中储存键值对对象。当两个不同的键对象的hashcode相同时,它们会储存在同一个bucket位置的链表中,可通过键对象的equals()方法来找到键值对。如果链表大小超过阈值(TREEIFY_THRESHOLD,8),链表就会被改造为树形结构。

在HashMap中,null可以作为键,这样的键只有一个,但可以有一个或多个键所对应的值为null。当get()方法返回null值时,即可以表示HashMap中没有该key,也可以表示该key所对应的value为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个key,应该用containsKey()方法来判断。而在Hashtable中,无论是key还是value都不能为null。

Hashtable是线程安全的,它的方法是同步的,可以直接用在多线程环境中。而HashMap则不是线程安全的,在多线程环境中,需要手动实现同步机制。

Hashtable与HashMap另一个区别是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException,但迭代器本身的remove()方法移除元素则不会抛出ConcurrentModificationException异常。但这并不是一个一定发生的行为,要看JVM。

先看一下简单的类图:

从类图中可以看出来在存储结构中ConcurrentHashMap比HashMap多出了一个类Segment,而Segment是一个可重入锁。

ConcurrentHashMap是使用了锁分段技术来保证线程安全的。

锁分段技术:首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。

ConcurrentHashMap提供了与Hashtable和SynchronizedMap不同的锁机制。Hashtable中采用的锁机制是一次锁住整个hash表,从而在同一时刻只能由一个线程对其进行操作;而ConcurrentHashMap中则是一次锁住一个桶。

ConcurrentHashMap默认将hash表分为16个桶,诸如get、put、remove等常用操作只锁住当前需要用到的桶。这样,原来只能一个线程进入,现在却能同时有16个写线程执行,并发性能的提升是显而易见的。


4、小试牛刀

讲一下HashMap哈HashTable的区别?HashTable和ConcurrentHashMap的区别?

相同点:

  • 底层数组 + 链表实现
  • 都可以用来存储 key-value 的数据

区别:

  • HashTable 无论key还是value都不能为null,线程安全
  • HashMap 可以存储null键和null值,线程不安全

我想线程安全但是我又想效率高?

使用 ConcurrentHashMap,其底层采用分段的数组+链表实现,线程安全,通过把 Map 分为 N 个 Segment(部分),可以提供相同的线程安全,但是效率提升N倍,默认提升16倍。

Hashtable 之所以效率低主要是使用了 synchronized 关键字对 put 等操作进行加锁,而 synchronized 关键字加锁是对整张 Hash 表的,即每次锁住整张表让线程独占,致使效率低下,而 ConcurrentHashMap 在对象中保存了一个 Segment 数组,即将整个Hash表划分为多个分段;而每个Segment元素,即每个分段则类似于一个Hashtable;这样,在执行put操作时首先根据hash算法定位到元素属于哪个Segment,然后对该Segment加锁即可,因此, ConcurrentHashMap 在多线程并发编程中可是实现多线程put操作。

面试必备:HashMap、Hashtable、ConcurrentHashMap的原理与区别相关推荐

  1. Java之HashMap系列--ConcurrentHashMap的原理

    原文网址:Java之HashMap系列--ConcurrentHashMap的原理_IT利刃出鞘的博客-CSDN博客 简介 本文介绍Java中的ConcurrentHashMap的原理. JDK7与J ...

  2. HashMap,HashTable,ConcurrentHashMap面试总结!!!

    原文:https://www.cnblogs.com/hexinwei1/p/10000779.html 一.小总结 1.HashMap .HashTable. ConcurrentHashMap H ...

  3. HashMap、Hashtable、ConcurrentHashMap的原理与区别

    HashTable 底层数组+链表实现,无论key还是value都不能为null,线程安全,实现线程安全的方式是在修改数据时锁住整个HashTable,效率低,ConcurrentHashMap做了相 ...

  4. Java基础学习总结(153)——HashMap、Hashtable、ConcurrentHashMap的原理与区别

    一.HashTable 底层数组+链表实现,无论key还是value都不能为null,线程安全,实现线程安全的方式是在修改数据时锁住整个HashTable,效率低,ConcurrentHashMap做 ...

  5. JAVA面试要点008---HashMap、Hashtable、ConcurrentHashMap的原理与区别_ConcurrentHashMap原理

    JAVA技术交流QQ群:170933152 下面直接来干货,先说这三个Map的区别: HashTable 底层数组+链表实现,无论key还是value都不能为null,线程安全,实现线程安全的方式是在 ...

  6. 面试必备:synchronized的底层原理?

    最近更新的XX必备系列适合直接背答案,不深究,不喜勿喷. 你能说简单说一下synchronize吗? 可别真简单一句话就说完了呀~ 参考回答: synchronize是java中的关键字,可以用来修饰 ...

  7. HashMap HashTable ConcurrentHashMap

    1. Hashtable 和 HashMap (1)区别,这两个类主要有以下几方面的不同: Hashtable和HashMap都实现了Map接口,但是Hashtable的实现是基于Dictionary ...

  8. HashMap,Hashtable,ConcurrentHashMap

    目录 一.多线程使用HashMap的一些线程安全问题 ①造成数据新增丢失 ②扩容时候,造成链表成环(jdk1.7版本) 二.Hashtable和HashMap的区别 ①核心方法加锁 ②其他语法上面的略 ...

  9. hashtable、hashmap、ConcurrentHashMap、treemap的区别

    http://blog.csdn.net/xin_jmail/article/details/24431835

最新文章

  1. Leetcode题解(20)
  2. 神策数据第五届“星斗奖”榜单公布!
  3. 为何被主流抛弃-江西IDC机房价格为何居高不下缺少竞争力-2014年5月江西IDC排行榜...
  4. 【Linux】一步一步学Linux——md5sum命令(61)
  5. pandas 判断是否等于nan_Python之pandas笔记
  6. java preference,Java使用Preference类保存上一次记录的方法
  7. 力扣401.二进制手表
  8. 一张图带你看懂 ,web前端开发应该知道的HTML5六大趋势
  9. CTF中遇到不知道文件类型_遇到孩子厌学不知道怎么沟通?做好这些策略,孩子肯定爱学...
  10. OSSIM5 自定义安装
  11. Java项目集成工作流引擎解决方案及实例展示(前后分离版)
  12. 在线重建索引 oracle,ORACLE重建索引详解
  13. 论文研读 —— 7. Very Deep Convolutional Networks for Large-Scale Image Recognition (3/3)
  14. js 声明——有无var的区别
  15. eulerOS 欧拉OS 审计修复日记
  16. 手机六大操作系统优缺点比较
  17. 往事如烟 - 简朴的老白树
  18. DirectX11--使用DirectX Tool Kit帮助开发
  19. 如何制定有效的项目计划,提高团队执行力
  20. 终于有人把 ZFS 文件系统讲明白了

热门文章

  1. 前端学习(2433):创建页面组件
  2. 前端学习(2244):计算器显示问题
  3. 前端学习(1766):前端调试值之内存监控的原理和方法
  4. 前端学习(1428):ajax封装三
  5. spring mvc学习(11):eclipse的环境maven项目创建前期工作
  6. spring学习(33):id和name
  7. 第二十七期:德国工业4.0眼里“工业互联网”与“智能制造”
  8. html:(18):文本输入框,密码输入框,文本域
  9. 玩转oracle 11g(29):检查操作系统版本和查询表中修改数据
  10. linux(2):linux命令查看开放哪些端口