HashMap、HashTable、ConcurrentHashMap 之间的区别

news2024/11/20 20:37:33

哈喽,大家好~我是保护小周ღ,本期为大家带来的是 HashMap、HashTable、ConcurrentHashMap 之间的区别,从数据结构到多线程安全~确定不来看看嘛~
更多精彩敬请期待:保护小周ღ *★,°*:.☆( ̄▽ ̄)/$:*.°★* ‘


一、 直观上的区别

HashTable 和 HashMap都是用于存储键值对的数据结构

我们一般把搜索的数据称之为关键字 (key), 与关键字 key 对应的为值(value),这种模型统称为

“key -value 的键值对”。

Map 和 Table 采用的都是 key - Value 模型,例如:有一个字符串,统计其中每个字符出现的次数,

 Java 中Map 中的数据结构:

画了一个简单的图理解一下,如果详细的介绍的话恐怕就会脱离主题了。 

哈希算法是HashMap的底层实现原理里面很重要的一个组成部分,它来决定数据的位置及访问记录,哈希算法也叫散列算法(Map 的底层数据的存储规则就是使用该算法),把我们存在map中的key(任意长度的值)通过此算法变成固定长度的key(地址),通过这个地址访问数据。

1. HashMap 和 HashTable 的第一点不同,底层数据结构不同: jdk1.7底层都是数组+链表的结构,但jdk1.8 HashMap加入了红黑树,

在JDK1.8之后,HashMap中的链表在满足以下两个条件时,将会转化为红黑树(自平衡的排序二叉搜索树):

1. 数组 array[ i ] 处存放的链表长度大于等于8;
2. 数组长度大于等于64;

满足以上两个条件,数组 array[ i ] 处的链表将自动转化为红黑树,其他位置如 array[ i ]处的数组元素仍为链表。当链表的长度低于 8 的时候又会由红黑树变成链表。

Map 的扩容机制 ,使用了负载因子调节,荷载因子 = 填入表中的元素个数 / 散列表的长度

Java 中限制了荷载因子的大小为 0.75,在实际开发中荷载因子根据业务需要而定,当荷载因子大于 0.75 的时候,意味着 “数组”的长度就需要扩容了,扩容意味着链表中的元素需要重新哈希(使用哈希函数重新计算元素位置并添加),这个开销是非常大的。


2. 初始化容量不同:HashMap 的初始容量为:16,Hashtable 初始容量为:11,两者的负载因子默认都是:0.75。
3. HashMap:在不指定容量的情况下的默认容量为16; 当已用容量>总容量 * 负载因子时,要求一定为2的整数次幂,扩容时将容量变为原来的2倍
Hashtable 扩容规则为当前容量翻倍 +1。

4. Hashtable:key和value都不允许出现null值; HashMap:null能够作为键(key),这样的键只有一个。


二、多线程环境使用哈希表

HashMap 是线程不安全的,在多线程的环境中,还是会存在多个线程并发对同一个共享数据进行操作从而引发的线程安全。

在多线程的环境下可以使用的哈希表有:Hashtable  和 ConcurrentHashMap 

接下来就围绕这两种哈希表来展开:

2.1 Hashtable 

Hashtable 为了保证线程安全问题,将每个关键的方法都加上了 synchronized 关键字,这就相当于针对 hashtable 对象本身加锁,多线程并发执行,同一单位时间内,只能有一个线程能(获取到对象锁,并加锁,一个对象只有一把锁,线程只有拿到锁才能够继续执行)争对 hashtable 进行操作,其他的线程阻塞等待,加锁操作有时间消耗,包括释放锁之后唤醒其他线程来竞争锁都是一笔开销,所以 Hashtable 整体在单线程中效率是低于 hashMap 的,在多线程的环境下效率也是极低的。

总结:

1. Hashtable保证线程的方式就是争对 Hashtable 对象本身加锁

2. 如果多线程访问同一个Hashtable 就会直接造成锁冲突。

3. 如果在多线程的环境下,一个线程在执行中触发了扩容机制,就会由该线程完成全部数据的重新哈希计算位置,涉及到大量的元素移动,在这个时间里,其他线程只能干等着,所以效率非常低。


2.1 ConcurrentHashMap 

这个哈希表可以看作是 hashMap 线程安全的版本,在 JDK 1.7 的时候跟 hashMap 一样都是

数组 + 链表的结构。

在线程安全的角度也是在 hashtable 的基础上做了一系列改进和优化,hashtable 是针对整个对象加锁,在 JDK1 .7 的时候 ConcurrentHashMap 采用的分段加锁的机制,对每一个“段”来加锁。

ConcurrentHashMap由segment 数组结构和 hashEntry 数组结构构成的,即 ConcurrentHashMap 把哈希桶数组切分成小数组(Segment ),每个小数组有 n 个 HashEntry 组成。

每个segment配备一把锁,当一个线程访问其中一段数据时,就会尝试获取 segment 中的对象锁,这样在保证segment锁住的同时而不影响其他线程访问其他的segment, 在保证安全性的同时也提高了效率。

Segment数组的长度是不会被改变的,初始化如果不规定,那么就采用默认的 16,扩容只是针对segment数组内部的HashEnrty数组。

JDK1.8下的ConcurrentHashMap

JDK 1.8以后 hashMap 和 ConcurrentHashMap 底层的结构都改为 数组 + 链表 + 红黑树的结构。

在线程安全方面又做出了进一步的提升

1. 对ConcurrentHashMap 的读操作没有加锁(get() ,根据key 返回 value 值这些只做查询的方法),而是使用了 volatile 关键字保证内存可见性(保证每次从内存中读取数据),只针对写操作加锁(put() 等方法),而且不是针对 map 对象加锁,也不是针对每一个段加锁,而是针对每个链表的头节点加锁(synchronized 关键字修饰)(每个对象都有一把锁,每个链表,都是一个list 对象,所以可以直接使用 list 的对象锁),这样就实现了更加细粒度的锁。

 写操作,只是针对链表的加锁。


2. ConcurrentHashMap 在保证线程安全的方面使用的是 CAS(可以看作是硬件支持的原子性操作的指令,适用于无锁编程,也可以保证线程安全问题) 和 synchronized 锁 ,在锁冲突不严重的场景下使用 CAS 自旋来更新数据,可以避免出现了重量级锁的情况。

3. 扩容机制:

  • 当某个线程对 ConcurrentHashMap 进行操作时如果触发了扩容机制,只需要创建一个新的 哈希表,将当前线程操作的链表的部分数据搬运过去,扩容期间,新、老哈希表都存在。
  • 后续每个来操作 ConcurrentHashMap 的线程,都会搬运部分元素,直到搬完老哈希表中最后一个元素,再将老哈希表删除。
  • 这个期间,插入元素只往新哈希表中添加,查找元素需要同时在新、老哈希表中查询。

ConcurrentHashMap 总结:

1. ConcurrentHashMap 的读操作不会加锁,目的是为了进一步降低锁冲突的概率,但是为了保证每次都能从内存中访问数据(防止其他线程对数据更新,当前线程还不知道)使用了 volatile 关键字保证内存可见性。

2. ConcurrentHashMap 的分段锁技术是 JDK 1.7 中采用的技术,就是把若干个哈希桶分成一个“段”(Segment),针对每个分段加锁。Segment数组的长度是不会被改变的,初始化如果不规定,那么就采用默认的 16,扩容只是针对segment数组内部的HashEnrty数组。数据结构结构采用 数组 + 链表的结构;

3. ConcurrentHashMap 在JDK 1.8 中取消了分段锁的安全机制,而是给每个哈希桶(每个链表)分配一个锁,在数据结构方面改进成数组 + 链表 + 红黑树的方式,当链表大于等于8 个元素就转换成红黑树


到这里,HashMap、HashTable、ConcurrentHashMap 之间的区别,博主已经分享完了,希望对大家有所帮助,如有不妥之处欢迎批评指正。

本期收录于博主的专栏——JavaEE,适用于编程初学者,感兴趣的朋友们可以订阅,查看其它“JavaEE基础知识”。

感谢每一个观看本篇文章的朋友,更多精彩敬请期待:保护小周ღ *★,°*:.☆( ̄▽ ̄)/$:*.°★* 

遇见你,所有的星星都落在我的头上……

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/428254.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

内存、CPU与指针的知识

在计算机中,内存、CPU和指针是非常重要的概念。在本篇博客中,我们将探讨内存、CPU和指针的知识。 内存的概念 内存是计算机中的一种存储设备,用于存储程序和数据。内存可以被CPU读取和写入,因此是计算机中非常重要的组成部分。在…

006:Mapbox GL添加zoom和旋转控件

第006个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+mapbox中添加zoom和旋转rotation控件 直接复制下面的 vue+mapbox源代码,操作2分钟即可运行实现效果 文章目录 示例效果配置方式示例源代码(共60行)相关API参考:专栏目标示例效果 配置方式 1)查看基础设置:h…

【数据结构第八章】- 排序(万字详解排序算法并用 C 语言实现)

目录 一、基本概念和排序方法概述 1.1 - 排序的基本概念 1.2 - 内部排序的分类 二、插入排序 2.1 - 直接插入排序 2.2 - 希尔排序 三、交换排序 3.1 - 冒泡排序 3.2 - 快速排序 3.2.1 - 递归算法 3.2.2 - 优化 3.2.3 - 非递归算法 四、选择排序 4.1 - 简单选择排…

关于统信UOS(Linux)系统磁盘无损扩容的方法

前言 针对某托管平台分配的4台虚拟服务器,操作系统统信UOS(Linux),数据磁盘空间已满,无损扩容的办法。 (在操作硬盘扩容前,为了安全起见,请通过磁盘快照功能备份服务器系统盘与数据盘…

Java 堆外内存

文章目录Java 堆外内存堆外内存的分配方式使用 Unsafe 类进行分配使用 ByteBuffer 进行分配堆外内存的查看方式Java 堆外内存 在 Java 虚拟机中,分配对象基本上都是在堆上进行的,然而在有些情况下,缓存的数据量非常大时,使用磁盘或…

【Python_Scrapy学习笔记(十四)】基于Scrapy框架的文件管道实现文件抓取(基于Scrapy框架实现多级页面的抓取)

基于Scrapy框架的文件管道实现文件抓取(基于Scrapy框架实现多级页面的抓取) 前言 本文中介绍 如何基于 Scrapy 框架的文件管道实现文件抓取(基于Scrapy框架实现多级页面的抓取),并以抓取 第一PPT 网站的 PPT 模板为例进行展示,同时抓取此网站数据的方式…

Docker安装Nexus搭建Maven私服及介绍

目录前言一、Nexus是什么?二、Docker安装方式1. 拉取镜像2. 创建挂载目录3. 运行4. 容器运行日志 (可选)三、用户登录四、仓库介绍五、创建代理仓库六、上传依赖(重点)七、下载依赖常见问题1、如何把新建的仓库添加到组…

【前端之旅】vue-router声明式导航和编程式导航

一名软件工程专业学生的前端之旅,记录自己对三件套(HTML、CSS、JavaScript)、Jquery、Ajax、Axios、Bootstrap、Node.js、Vue、小程序开发(Uniapp)以及各种UI组件库、前端框架的学习。 【前端之旅】Web基础与开发工具 【前端之旅】手把手教你安装VS Code并附上超实用插件…

科研作图-常用的图像分割指标 (Dice, Iou, Hausdorff) 及其计算

1. 简介 本节内容主要是介绍图像分割中常用指标的定义、公式和代码。常用的指标有Dice、Jaccard、Hausdorff Distance、IOU以及科研作图-Accuracy,F1,Precision,Sensitive中已经介绍的像素准确率等指标。在每个指标介绍时,会使用编写相关代码,以及使用M…

TypeScript学习记录Ts基础

安装及初步使用 1.CMD全局安装 npm install -g typescript2.检查是否安装成功 tsc -V 如报错tsc 不是内部或外部命令,也不是可运行的程序或批处理文件[可参考](https://www.cnblogs.com/sanyekui/p/13157918.html)3.在vscode中新建ts文件01_ts.ts let str:strin…

iPhone上5款视频压缩工具推荐,学会轻松压缩视频

随着技术的不断进步,现在移动端设备已经能够轻松地拍摄高清视频,然而这也带来了存储压力的增大。苹果手机更新换代频繁,但内存空间却没有跟着变大,因此如何压缩视频成为了一个重要的问题。 苹果手机怎么压缩视频?有什…

01-ASPICE体系知识

1. ASPICE是什么? ASPICE: 全称是"Automotive Software Process Improvement and Capacity Determination",汽车软件过程改进及能力评定,是汽车行业用于评价软件开发团队的研发能力水平的模型框架。最初由欧洲20多家主要汽车制造商…

上位机:创建WPF应用并使用控件完成控件交互

创建WPF应用并使用控件完成控件交互 1.XAML是WPF技术中专门用于设计UI的语言。 2.优点,XAML是一种声明型语言,只能用来声明一些UI元素、绘制UI和动画,不能在其中加入程序逻辑。实现UI与逻辑的剥离。 下面使用visual studio2019建立一个WPF项目: 进入项目,点击运行,然后…

FluxMQ—引领物联网新时代的高性能MQTT网关

FluxMQ—引领物联网新时代的高性能MQTT网关 随着物联网技术的快速发展,人们越来越意识到实时、可靠、安全的数据传输对于智能化的生产与生活的重要性。因此,市场对于高性能的物联网数据传输解决方案有着强烈的需求。FluxMQ正是为满足这一需求而诞生的一…

苹果电脑怎么用移动硬盘ntfs 苹果电脑移动硬盘怎么退出

Mac电脑默认不支持写入NTFS格式移动硬盘,这导致很多Mac用户的工作过程遇到很多不必要的麻烦。如何才能让Mac电脑“永久”拥有写入NTFS格式移动硬盘的权限呢?不少用户使用完移动硬盘后直接拔出,这可能会导致未保存的文件丢失。使用完移动硬盘后…

城市供水绩效指标解释

1.城市供水绩效评价指标体系构成 1.1.1绩效指标体系横向构成包括: 背景信息:用于计算指标的数据。 解释性因素:靠管理不易改变的数据。 绩效指标:若干个基础数据的综合运算结果,用于评价所提供服务的有效性…

Python每日一练(20230418)

目录 1. 将有序数组转换为二叉搜索树 🌟🌟 2. 四数之和 🌟🌟 3. 排序数组查找元素的首末位置 🌟🌟 🌟 每日一练刷题专栏 🌟 Golang每日一练 专栏 Python每日一练 专栏 C/C…

JAVA基于局域网的聊天室系统的设计与实现

本文介绍了采用JAVA编程开发视频聊天系统的一套比较常用的解决方案。文字聊天采用TCP模式;语音视频聊天采用UDP模式,在客户端之间点对点的进行。在该方案中,通过函数库VFW来实现视频捕获、影像压缩以及影像播放。微软公司提供的专门用于视频捕…

Python入门教程+项目实战-10.2节: 列表的操作符

目录 10.2.1 列表中的常用操作符 10.2.2 []操作符: 索引访问列表 10.2.3 [:]操作符:列表的切片 10.2.4 操作符:列表的加法 10.2.5 *操作符:列表的乘法 10.2.6 列表的关系运算 10.2.7 in操作符:查找元素 10.2.8 知识要点 …

香港电讯牌照申请介绍

香港对无线电发射器具的进出口实施法律管制.《电讯条例》规定进出口手提电话的人士须持有许可证或无线电商牌照。例如:手机、3C类产品、手提电脑等无线电子产品,通关时须要出示的证件之一。 一、香港电讯牌照申请需提供资料:1、有…