HashTable HashMap ConcurrentHashMap 的介绍以及区别

news2024/11/26 7:50:04

目录

🐇今日良言:投资自己才是最好的投资

🐉一.HashMap.

🐕二.HashTable

🐍三.ConcurrentHashMap

🐂四.三者的区别


🐇今日良言:投资自己才是最好的投资

 时隔四十多天,今天博主要更新了. 后续内容也是精华满满,希望对大家都有帮助.

这篇博客主要介绍的是 HashTable HashMap ConcurrentHashMap 之间的区别,重点要知道ConcurrentHashMap的优点(好处/与HashTable相比的优点)

🐉一.HashMap

1.概念和场景

在介绍HashMap之前,先来介绍一下什么是Map:

Map是一种专门用来搜索的容器或者数据结构,其具体的搜索效率与其具体的实例化子类有关.

在我们之前的学习过的内容中,常见的搜索方式有:

1).直接遍历  时间复杂度为O(N),元素较多时,效率会非常慢

2).二分查找  时间复杂度为O(logN),但搜索前必须要求序列是有序的

上述这两种方式都适合静态类型的查找,即:一般不会对区间进行插入和删除操作.而现实中的很多查找,如:

1).根据姓名查找成绩

2).通讯录,即根据姓名查询联系方式

3).不重复集合,即需要先搜索关键字是否已经在集合中

可能在查找时进行一些插入和删除的操作,也就是动态查找,此时上述的两种查找方式就不适合了,而Map是一种适合动态查找的集合容器

2.模型

一般把搜索的数据叫做关键字(Key),和关键字对应的称为值(Value),将其称为 Key-Value键值对,所以,模型一般有两种:

1).纯Key模型

    比如:

    有一个英文字典,快速查找一个单词是否在词典中

    快速查找某个名字在不在通讯录中

2).Key-Value模型

    比如:

     梁山好汉的江湖绰号(每个好汉都对应一个绰号)

Map中存储的就是Key-Value的键值对,Set中只存储了Key

 

 3.关于Map的说明

Map是一个接口类,没有继承自Collection,该类中存储的是<K,V>键值对,并且K一定是唯一的,而且不能重复.

关于Map.Entry<K,V> 的说明

Map.Entry<K,V> 是map内部实现的用来存放<Key,Value>键值对映射关系的内部类,该内部类中主要提供提供了<key,value>的获取,value的设置以及Key的比较方式

方法                                        解释
K getKey ()                     返回 entry 中的 key
V getValue ()                  返回 entry 中的 value
V setValue(V value)       将键值对中的 value 替换为指定 value
通过下面的代码以及运行结果增加理解

public class Exercise {
    public static void main(String[] args) {
        Map<String,String> map = new HashMap<>();
         map.put("宋江","及时雨");
         map.put("鲁智深","花和尚");
         Set<Map.Entry<String,String>> set = map.entrySet();
         for (Map.Entry<String,String> entry:set) {
            System.out.println("K:"+entry.getKey()+"  V:"+entry.getValue());
         }
    }
}

 

 

public class Exercise {
    public static void main(String[] args) {
        Map<String,String> map = new HashMap<>();
         map.put("宋江","及时雨");
         map.put("鲁智深","花和尚");
         Set<Map.Entry<String,String>> set = map.entrySet();
        for (Map.Entry<String,String> entry:set) {
            entry.setValue("好人");
        }
        for (Map.Entry<String,String> entry:set) {
            System.out.println("K:"+entry.getKey()+"  V:"+entry.getValue());
        }
    }
}

 

注:Map.Entry<K,V>并没有提供设置Key的方法

 4.Map的常用方法说明

注: 

1).Map是一个接口,不能直接实例化对象,如果要实例化对象,只能实例化其实现类TreeMap或者HashMap

2).Map中存放的键值对的Key是位移的,Value可以重复

3).Map中的Key可以全部分离出来存储到Set中(Key值不重复)

4).Map中的Value可以全部分离出来,存储在Collection的任何一个子集合中(value可能有重复)

5).Map中键值对的Key不能直接修改,Value可以修改,如果要修改Key,只能先将Key删除,然后再重新进行插入

5.HashMap的使用案例

public class Exercise {
    public static void main(String[] args) {
            Map<String, String> m = new HashMap<>();
    // put(key, value):插入key-value的键值对
    // 如果key不存在,会将key-value的键值对插入到map中,返回null
            m.put("林冲", "豹子头");
            m.put("鲁智深", "花和尚");
            m.put("武松", "行者");
            m.put("宋江", "及时雨");
            String str = m.put("李逵", "黑旋风");
            System.out.println(m.size());
            System.out.println(m);
    // put(key,value): 注意key不能为空,但是value可以为空
    // key如果为空,会抛出空指针异常
    //m.put(null, "花名");
            str = m.put("无名", null);
            System.out.println(m.size());
    // put(key, value):
    // 如果key存在,会使用value替换原来key所对应的value,返回旧value
            str = m.put("李逵", "铁牛");
    // get(key): 返回key所对应的value
    // 如果key存在,返回key所对应的value
    // 如果key不存在,返回null
            System.out.println(m.get("鲁智深"));
            System.out.println(m.get("史进"));
    //GetOrDefault(): 如果key存在,返回与key所对应的value,如果key不存在,返回一个默认值
            System.out.println(m.getOrDefault("李逵", "铁牛"));
            System.out.println(m.getOrDefault("史进", "九纹龙"));
            System.out.println(m.size());
    //containKey(key):检测key是否包含在Map中,时间复杂度:O(logN)
    // 按照红黑树的性质来进行查找
    // 找到返回true,否则返回false
            System.out.println(m.containsKey("林冲"));
            System.out.println(m.containsKey("史进"));
    // containValue(value): 检测value是否包含在Map中,时间复杂度: O(N)
    // 找到返回true,否则返回false
            System.out.println(m.containsValue("豹子头"));
            System.out.println(m.containsValue("九纹龙"));
    // 打印所有的key
    // keySet是将map中的key防止在Set中返回的
            for(String s : m.keySet()){
                System.out.print(s + " ");
            }
            System.out.println();
    // 打印所有的value
    // values()是将map中的value放在collect的一个集合中返回的
            for(String s : m.values()){
                System.out.print(s + " ");
            }
            System.out.println();
    // 打印所有的键值对
    // entrySet(): 将Map中的键值对放在Set中返回了
            for(Map.Entry<String, String> entry : m.entrySet()){
                System.out.println(entry.getKey() + "--->" + entry.getValue());
            }
            System.out.println();
        }
}

 运行结果: 

🐕二.HashTable

HashTable 继承了Dictionary抽象类,是一个Dictionary抽象类的具体实现,Dictionary是声明了操作"键值对"的函数接口的抽象类

哈希表的代码实现博主之前写过博客:

(4条消息) 哈希表(限定版)_程序猿小马的博客-CSDN博客

HashTable  定义了四种构造方法

1).默认构造方法

   Hashtable

2).传入一个参数,指定哈希表的大小

    Hashtable(int size)

3).传入两个参数,第一个参数指定哈希表的大小,第二个参数指定负载因子(即:达到负载因子,哈希表中就不能再添加元素,需要扩容)

    Hashtable(int size,float fillRatio)

4).创建了一个以M中元素为初始化元素的哈希表。

哈希表的容量被设置为M的两倍。

    Hashtable(Map m)

HashMap除了从Map接口中定义的方法外,还包含以下方法:

🐍三.ConcurrentHashMap

  ConcurrentHashMap是更优化的线程安全哈希表,主要使用于多线程中

  对于ConcurrentHashMap的详细介绍放到下面与HashTable区别中

🐂四.三者的区别

1.HashTable 和 ConcurrentHashMap的区别

1).ConcurrentHashMap相较于HashTable 大大缩小了锁冲突的概率,具体来讲就是将一把大锁转换成了多把小锁.

   HashTable的做法是在方法上直接加synchronized,等于是给this加锁.只要操作哈希表上的任意元素,都会产生加锁,也就都可能发生锁冲突.

   ConcurrentHashMap的做法:让每条链表有各自的锁(而不是公用一把大锁),具体来说就是:使用每个链表的头结点作为锁对象.(在jdk1.8之前ConcurrentHashMap使用的是"分段锁",也就是是给几条链表加锁,从而实现缩小锁冲突的概率,但是这种做法不够彻底,一方面粒度切分的还不够细,另一方面代码实现也更繁琐)

2).ConcurrentHashMap做了一个激进的操作,针对读操作不加锁,针对写操作加锁.

     具体来说就是:

     a.两个线程如果都进行读取变量的操作,不发生冲突(读和读操作之间没有冲突)

     b.两个线程如果都进行修改变量的操作,发生冲突(写和写之间发生冲突)

     c.如果一个线程进行读取变量的操作,一个线程进行修改变量的操作,不发生冲突.

     第三种情况中,必须要求写操作是原子的,并且变量是被voltile修饰的,否则就会发生类似"脏读"的情况

    脏读属于数据库事务的知识点,博主之前的博客有过详细介绍:

  (4条消息) 如何理解数据库事务?_程序猿小马的博客-CSDN博客

   volatile关键字,博主在之前的博客也有过详细介绍,主要是解决内存可见性问题和禁止指令重排序

    (4条消息) 线程安全问题_程序猿小马的博客-CSDN博客

3).ConcurrentHashMap内部充分的使用了CAS,通过这个进一步的削减加锁操作的数目

    (CAS博主在后面的文章会有具体介绍,后续会附上博客链接)

4).二者对于扩容的方式不同

    HashTable/HashMap 扩容:

    创建一个更大的数组空间,将旧数组上的每条链表上的每个元素都搬运到新的数组上,主要进行的操作就是插入和删除,这个扩容操作会在某次put操作的时候发生,也就是达到负载因子时,此时,如果旧数组中的元素特别多的话,就导致这次的put操作比平常的put操作要慢很多倍

     ConcurrentHashMap 扩容:

     采用的是"化整为零"的方式,也就是每次搬运一小部分

     创建新的数组,旧数组也保留.

     每次put操作,都往新数组上添加,同时进行一部分的搬运(将一部分旧数组上的元素搬运到新的数组上)

     每次get的时候,新数组和旧数组都查询.

     每次remove的时候,直接删除即可

     经过一定时间之后,所有的元素都搬运好了,此时就释放旧数组

2.HashTable和HashMap的区别

1).父类不同

 HashTable的父类是Dictionary,HashMap的父类是AbstractMap

2).线程安全

  HashTable是线程安全的,HashMap是线程不安全的

  这就意味着,在单线程下HashMap的性能要比HashTable要好

  如果想要线程安全的HashMap,可以通过Collections类的静态方法synchronizedMap获得线程安全的HashMap。

3.HashMap和ConcurrentHashMap的区别

 1).线程安全

     ConcurrentHashMap是线程安全的,而HashMap是线程不安全的

2).并发操作

     ConcurrentHashMap支持并发操作,HashMap不支持并发操作

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

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

相关文章

[机器学习]损失函数DLC

一、损失函数的概念 损失函数(Loss Function)是用于评估预测结果和真实结果之间差距的一个公式&#xff0c;为模型优化指明方向。在模型优化过程中一般表述为&#xff1a;或 与针对整个训练集的代价函数(Cost Function)不同&#xff0c;损失函数通常仅针对单个训练样本。可以归…

RK3568平台开发系列讲解(驱动基础篇)Linux 内核源码介绍

🚀返回专栏总目录 文章目录 一、目录树概览二、快速确定主板关联代码2.1、基础代码2.2、驱动代码沉淀、分享、成长,让自己和他人都能有所收获!😄 📢进行嵌入式 Linux 产品开发,往往需要对内核进行裁剪和定制,以满足嵌入式产品的功能和性能需求。 一、目录树概览 解压…

Python---学生管理系统(pyinstaller)

专栏&#xff1a;python 个人主页&#xff1a;HaiFan. 专栏简介&#xff1a;本专栏主要更新一些python的基础知识&#xff0c;也会实现一些小游戏和通讯录&#xff0c;学时管理系统之类的&#xff0c;有兴趣的朋友可以关注一下。 学生管理系统前言创建入口函数新增学生insert展…

行为型模式-迭代器模式

1.概述 定义&#xff1a;提供一个对象来顺序访问聚合对象中的一系列数据&#xff0c;而不暴露聚合对象的内部表示。 2.结构 迭代器模式主要包含以下角色&#xff1a; 抽象聚合&#xff08;Aggregate&#xff09;角色&#xff1a;定义存储、添加、删除聚合元素以及创建迭代器…

实战案例:Python批量识别银行卡号码并且写入Excel,初学者也可以轻松使用~

大家好&#xff0c;这里是程序员晚枫&#xff0c; 今天我们继续学习Python自动化办公&#xff1a;每次有新员工入职&#xff0c;都要收集大量的工资卡信息&#xff0c;并且生成Excel文档&#xff0c;能不能用Python准确、快速地解决呢&#xff1f; 今天我们就来学习一下&…

【CCNA | 网络模拟器CPT系列】Cisco Packet Tracer 8.2.0 的安装 Ⅰ

目录1. 下载 Cisco Packet Tracer2. 安装 Cisco Packet Tracer&#xff08;1&#xff09;许可协议界面&#xff08;2&#xff09;选择安装目录&#xff08;3&#xff09;选择开始菜单文件夹&#xff08;4&#xff09;选择附加任务&#xff08;5&#xff09;确认设置选择&#x…

K8S环境安装

K8S环境安装 下面是环境的主机名和IP 主机名ipk8smaster192.168.68.150k8snode1192.168.68.151k8snode2192.168.68.152 1、安装docker 配置yum源 sudo yum install -y yum-utils sudo yum-config-manager \ --add-repo \ http://mirrors.aliyun.com/docker-ce/linux/centos/d…

不受支持的 Mac 上的通用控制(现已支持 macOS Ventura)

现已支持 macOS Ventura 请访问原文链接&#xff1a;不受支持的 Mac 上的通用控制&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;www.sysin.org 本文为 在不受支持的 Mac 上安装 macOS Ventura、Monterey、Big Sur (OpenCore Legacy Pa…

【Effective_Objective-C_5内存管理】

文章目录前言29.理解引用计数引用计数的工作原理关闭ARC模式引用计数的增减理解一下引用计数存在属性存取方法中的内存管理自动释放池保留环要点30.以ARC简化引用计数使用ARC时必须遵循的方法和命名规则-变量的内存管理语法意义ARC如何清理实例变量覆写内存管理的方法要点31.在…

eclipse调试没反应,没有Debug窗口弹出

用eclipse调试&#xff0c;点击“Run”“Debug as”没有反应&#xff0c;debug视图没有弹出一、打开Preferences点击“Windows”&#xff0c;点击“Preferences”&#xff0c;找到“Run/Debug”下面的“Perspectives”&#xff0c;我的打开是这个样子&#xff0c;要修改一些选项…

【JavaSE成神之路】一文搞定static关键字

哈喽&#xff0c;我是兔哥呀&#xff0c;今天就让我们继续这个JavaSE成神之路&#xff01; 这一节啊&#xff0c;咱们要学习的内容是Java的static关键字。 1. 我们是怎么使用static关键字的 我们其实一直在用static关键字&#xff0c;比如main方法&#xff1a; public stati…

CSS样式基础内容3

目录 CSS三大特性 层叠性 继承性 行高的继承性 优先级 权重的叠加 CSS盒子模型 border边框 边框的复合写法 表格的细线边框 边框会影响盒子的实际大小 内边距 padding会影响盒子实际大小 网页导航案例 外边距 外边距合并 相邻块元素垂直外边距的合并 清除内外…

go语言实战(猜数字+在线词典+服务器)

go语言实战案例1.猜数字游戏2. 词典2.1 request2.2 response2.3 修改写死的单词为用户可写的word2.4 细节优化2.4.1 防止403、404等状态码2.4.2 增强输出可读性2.5 在线词典的最终代码3.SOCKS5代理服务器3.1 tcp echo server3.2 验证3.3 请求3.4 完整代理实现作业1.修改第一个猜…

cadence SPB17.4 S032 - allegro出的槽孔文件不用做任何处理就可以交给板厂生产

文章目录cadence SPB17.4 S032 - allegro出的槽孔文件不用做任何处理就可以交给板厂生产前言备注补充 - CAM350V14.6 - 在win10 22H2下不能正常用ENDcadence SPB17.4 S032 - allegro出的槽孔文件不用做任何处理就可以交给板厂生产 前言 以前交给板厂gerber文件时, 有一次, 板…

Pandas学习笔记-Day1安装与文件读取

Day1 安装与文件读取了解与安装什么是pandas?安装pandas?pandas数据读取读取mysql数据库了解与安装 什么是pandas? 处理数据一般分为几个阶段&#xff1a;数据整理与清洗、数据分析与建模、数据可视化与制表&#xff0c;Pandas 是处理数据的理想工具。 安装pandas? 如果…

自动驾驶感知——图像数据处理数学方法

文章目录1. 二值化操作2. 卷积操作3. 均值滤波4. 高斯滤波5. 图像梯度算子5.1 Prewitt梯度算子5.2 Sobel梯度算子5.3 Laplace二阶梯度6. 边缘特征点提取算子7. 基于规则的特征点提取8. 最小二乘拟合方法9. RANSAC曲线拟合10. Hough Transform 霍夫变换11. 基于学习的特征点提取…

图论(4)Floyd算法

一、概述 floyd算法主要作用有&#xff1a;1.找最短路 2.求传递闭包 3.找最小环 4.求出恰好经过k条边的最短路 本文章将介绍floyd求最短路的证明以及以上四个作用的实践。 二、floyd算法求最短路的证明 之前就多次提到过图论与dp问题的联系&#xff0c;floyd算法可以…

Go依赖管理

"做讨厌潮汐的稚童&#xff0c;祈祷月球失踪。"一、背景我们写一个程序&#xff0c;例如输出hello world 或者 一个猜数字游戏&#xff0c;这些用到的单体函数接口&#xff0c;只需要依赖一些原生的SDK即可。但是&#xff0c;面对复杂的实际问题、工程&#xff0c;仅…

使用构建工具创建Vue项目

使用构建工具创建Vue项目一、使用vue-cli脚手架构建vue项目创建步骤&#xff1a;二、使用 Vite构建vue项目创建步骤&#xff1a;一、使用vue-cli脚手架构建vue项目 Vue CLI 是一个基于 Vue.js 进行快速开发的完整系统&#xff0c;提供&#xff1a; 1 通过 vue/cli 实现的交互式…

演讲比赛流程管理系统

1. 演讲比赛程序需求 1.2程序功能 2. 项目创建 创建名为speech_contest的目录名称 3. 创建管理类 功能描述&#xff1a; 提供菜单界面与用户交互 对演讲比赛流程进行控制 与文件的读写交互 3.1 创建文件 在头文件和源文件的文件夹下分别创建speech…