JavaSE从零开始到精通(九) - 双列集合

news2024/9/24 3:20:26

 1.前言

Java 中的双列集合主要指的是可以存储键值对的集合类型,其中最常用的包括 Map 接口及其实现类。这些集合允许你以键值对的形式存储和管理数据,提供了便捷的按键访问值的方式。

2. HashMap 

HashMap 是基于哈希表实现的 Map 接口的类,允许存储键值对,支持 null 键和 null 值。它提供了 O(1) 时间复杂度的插入、删除和查找操作,但不保证映射顺序。

由键决定:无序、不重复、无索引。

底层数据结构: 数组 + 链表 / 红黑树(JDK8+)其中一个链表超过8,并且数组长度不低于64转换为红黑树。通过哈希函数将键映射到数组索引,解决哈希冲突的链表或红黑树存储。初始容量是16,扩容因子是0.75。扩容扩大两倍(持续保持2^n次方的容量)。

多个输入对应相同输出:hash冲突

为什么要转换成红黑树?

HashMap变红黑树主要用于防患于未然,防止有些人造一些恶劣数据。正常存20万个单词都不会有链表超过8。

为什么hash表的容量是2^n次方?

1.能使用2^n

对于程序来说涉及位运算符相对来说都是高效的,因为计算机底层是以二进制存储的,二进制是天然的位运算符操作容器。 

2.高低位异或计算hash值

因为二进制和容器容量正好对应,只需要看后几位,高位根本用不上。2^n次方看后n位。

低位相同的哈希码,在高位不同的情况下,会产生hash冲突。通过高16位和低16位异或运算能够让存入的位置由高位的参与决定。

负载因子的概念

负载因子(load factor)是哈希表的一个重要概念,其定义为哈希表的元素数量除以桶数量,用于衡量哈希冲突的严重程度,也常作为哈希表扩容的触发条件。例如在 Java 中,当负载因子超过 0.75 时,系统会将哈希表扩容至原先的 2 倍。

容易想到,哈希表容量 𝑛 越大,多个 key 被分配到同一个桶中的概率就越低,冲突就越少。因此,我们可以通过扩容哈希表来减少哈希冲突。

类似于数组扩容,哈希表扩容需将所有键值对从原哈希表迁移至新哈希表,非常耗时;并且由于哈希表容量 capacity 改变,我们需要通过哈希函数来重新计算所有键值对的存储位置,这进一步增加了扩容过程的计算开销。为此,编程语言通常会预留足够大的哈希表容量,防止频繁扩容。

HashSet 的加载因子被设置为 0.75 是为了在空间利用率和性能之间取得平衡。这个值能够使得哈希表在大多数情况下既能够有效地利用内存,又能够保持较好的查询性能,是经过权衡和实验确定的合理选择。

// 创建一个HashMap
Map<String, Integer> hashMap = new HashMap<>();

// 添加键值对
hashMap.put("apple", 1);
hashMap.put("banana", 2);

// 获取值
int value = hashMap.get("apple");
System.out.println(value); // 输出: 1

3. LinkedHashMap:

  • LinkedHashMap 继承自 HashMap,保留了元素插入顺序,即按照插入顺序迭代元素。可选择按访问顺序(LRU 缓存算法)排序。

  • 由键决定:有序、不重复、无索引。

    底层数据结构: 哈希表 + 双向链表。双向链表维护插入顺序或访问顺序。

 

与HashMap不同的是:存和取的顺序相同

原理:每个键值对元素之间又额外的多了一个双链表的机制记录存储的顺序。 

 在添加第一个元素的时候,会记录第一个元素的地址值,然后取的时候根据第一个元素的地址值找到头节点,依次进行取元素。

// 创建一个LinkedHashMap,保持插入顺序
Map<String, Integer> linkedHashMap = new LinkedHashMap<>(16, 0.75f, true);

// 添加键值对
linkedHashMap.put("apple", 1);
linkedHashMap.put("banana", 2);

// 获取值,更新访问顺序
int value = linkedHashMap.get("apple");
System.out.println(value); // 输出: 1

4. TreeMap:

  • TreeMap 基于红黑树实现,可根据键的自然顺序或自定义比较器排序。元素有序存储,提供灵活的自定义排序功能。

    底层数据结构: 红黑树。自平衡二叉查找树,保证有序性。

 对于原理和TreeSet相似,在上一篇已经讲解过了,这里就不过多介绍了。

// 创建一个TreeMap,根据键的自然顺序排序
Map<String, Integer> treeMap = new TreeMap<>();

// 添加键值对
treeMap.put("apple", 1);
treeMap.put("banana", 2);

// 获取值
int value = treeMap.get("apple");
System.out.println(value); // 输出: 1

5. Hashtable:

  • Hashtable 是早期的 Map 类,与 HashMap 类似但线程安全。由于同步开销大,推荐使用 ConcurrentHashMap 替代。

    底层数据结构: 数组 + 链表。通过哈希函数将键映射到数组索引,使用链表解决冲突。

// 创建一个Hashtable
Map<String, Integer> hashtable = new Hashtable<>();

// 添加键值对
hashtable.put("apple", 1);
hashtable.put("banana", 2);

// 获取值
int value = hashtable.get("apple");
System.out.println(value); // 输出: 1

面试题:谈谈HashMap和HashTable的区别

  1. 线程安全方面
    1. HashTable是线程安全的(底层使用了全局同步锁)
    2. HashMap是线程不安全的,因此可以得出HashMap的性能比较好
  2. 数据结构的实现
    1. java8以后HashMap使用数组+链表+红黑树组成(当节点高于7转换为红黑树)

    2. HashTable是数组+链表

  3. 初始容量
    1. HashMap初始容量是16
    2. HashTable初始容量是11
  4. hash算法
    1. HashMap的散列算法是对key的hashcode进行二次散列,从而避免key的分布不均匀影响性能。
    2. HashTable是hashcode对数组的长度取模

6. ConcurrentHashMap:

  • ConcurrentHashMap 针对高并发设计,通过分段锁(Segmented lock)实现线程安全。性能优于 Hashtable,在多线程环境中使用较为合适。

    底层数据结构: 分段锁 + 数组 + 链表 / 红黑树(JDK8+)。每个段(Segment)独立锁定,允许多线程并发访问不同段的数据。

// 创建一个ConcurrentHashMap
Map<String, Integer> concurrentHashMap = new ConcurrentHashMap<>();

// 添加键值对
concurrentHashMap.put("apple", 1);
concurrentHashMap.put("banana", 2);

// 获取值
int value = concurrentHashMap.get("apple");
System.out.println(value); // 输出: 1

这些双列集合类在不同的场景中提供了各自的优势,根据需求选择合适的实现类可以提高数据的管理效率和性能。

面试题:HashTable为什么会被ConcurrentHashMap替代

ConcurrentHashMap 可以替代 Hashtable 的几个主要原因包括:

  1. 并发性能优化: ConcurrentHashMap 使用了分段锁(Segmented lock)或 CAS 操作来实现线程安全,而 Hashtable 使用了全局锁。这意味着 ConcurrentHashMap 在多线程并发访问时,可以支持更高的并发度,因为不同段(Segment)的数据可以并行操作,而 Hashtable 的所有操作都是同步的。

  2. 更好的扩展性: ConcurrentHashMap 在设计上采用了分段锁策略,这样在多线程情况下,不同段的数据可以并行处理,从而减少了锁竞争,提高了并发访问效率。相比之下,Hashtable 的同步操作可能会导致线程争用,限制了扩展性和性能。

  3. 更好的迭代性能: 在迭代时,ConcurrentHashMap 可以允许在不进行任何同步的情况下进行读取操作,而 Hashtable 则要求在整个表上进行锁定。这意味着在迭代期间,ConcurrentHashMap 的性能更好,因为它不会被其他线程的修改所阻塞。

  4. 允许空键和空值: ConcurrentHashMap 允许 null 键和 null 值的存在,而 Hashtable 不允许。这在某些场景下可能更为灵活。

综上所述,ConcurrentHashMap 在设计和实现上优于 Hashtable,尤其是在高并发环境中能够提供更好的性能和扩展性,因此在大多数情况下推荐使用 ConcurrentHashMap 而不是 Hashtable。

 7. Peoperties

Properties 是 Java 中处理配置文件的一个常用类,它继承自 Hashtable<Object, Object>,也实现了 Map<Object, Object> 接口。主要用于存储键值对形式的配置信息,通常用于读取和写入属性文件(.properties 文件)。 

Properties 类在 Java 中广泛应用于配置文件的读写

使用load()方法从一个输入流中加载属性,或者使用store()方法将属性保存到输出流中。

主要特点和用途:

  1. 存储键值对: Properties 对象存储的是字符串键和值的映射关系,键和值都必须是字符串类型。

  2. 加载和存储属性文件: Properties 可以从文件中加载配置信息,也可以将配置信息写入文件。常见的配置文件格式是 .properties 文件,它采用 key=value 的格式存储配置信息。

  3. 默认的读写方法: 提供了便捷的方法来加载和存储属性文件,如 load()store() 方法。

  4. 默认值支持: 可以设置默认值,当请求的属性不存在时,返回默认值。

  5. 线程安全性: Properties 继承自 Hashtable,因此默认不是线程安全的。在多线程环境下,可以通过同步措施来保证线程安全。

基本操作示例:

创建和设置属性:
Properties prop = new Properties();
prop.setProperty("database.url", "jdbc:mysql://localhost:3306/mydatabase");
prop.setProperty("database.user", "username");
prop.setProperty("database.password", "password");
从文件加载属性: 
try (FileInputStream fis = new FileInputStream("config.properties")) {
    prop.load(fis);
} catch (IOException e) {
    e.printStackTrace();
}
 将属性存储到文件:
try (FileOutputStream fos = new FileOutputStream("config.properties")) {
    prop.store(fos, "Database Configuration");
} catch (IOException e) {
    e.printStackTrace();
}
 获取属性值:
String url = prop.getProperty("database.url");
String user = prop.getProperty("database.user", "defaultUser"); // 指定默认值
String password = prop.getProperty("database.password");
注意事项:
  1. 字符编码:默认情况下,store() 方法使用 ISO 8859-1 编码,不支持 Unicode 字符。如果需要支持 Unicode,可以使用 Writer 参数的 store(Writer writer, String comments) 方法,并指定合适的字符编码。
  2. 属性文件格式: .properties 文件使用简单的 key=value 格式存储数据,不支持复杂的数据结构或注释(可以用 # 开头注释)。
  3. 默认值处理: 如果请求的属性不存在,getProperty(key) 方法返回 null;可以使用 getProperty(key, defaultValue) 指定默认值。

 8. 总结

        双列集合在软件开发中扮演关键角色,通过键值对的方式存储和管理数据,支持快速的插入、删除和查找操作,适用于各种应用场景,如配置管理、缓存实现和关联数据管理。Java中的Map接口及其实现类(如HashMap、TreeMap等)提供了多样化选择,开发人员可以根据需求选择最适合的实现以提高性能和效率。这些特性使得双列集合成为处理复杂数据结构和优化数据访问的重要工具。

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

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

相关文章

软件测试中的压力测试和性能测试区别

压力测试和性能测试是软件测试中两种重要的测试类型&#xff0c;它们都旨在评估软件在不同条件下的表现&#xff0c;但侧重点和目的有所不同。 压力测试&#xff08;Stress Testing&#xff09;定义&#xff1a; 压力测试是一种测试方法&#xff0c;用于确定软件在极端条件下…

【C++指南】类和对象(上)

&#x1f493; 博客主页&#xff1a;倔强的石头的CSDN主页 &#x1f4dd;Gitee主页&#xff1a;倔强的石头的gitee主页 ⏩ 文章专栏&#xff1a;《数据结构与算法》 期待您的关注

网格大师将OSGB转3Dtiles是否用了LOD以及顶层重建?

答&#xff1a;如果勾选合并根节点&#xff0c;是有用到LOD以及顶层重建的。 网格大师是一款能够解决实景三维模型空间参考、原点、瓦块大小不统一&#xff0c;重叠区域处理问题的工具“百宝箱”&#xff0c;集格式转换、坐标转换、轻量化、瓦片重划分等多功能优势于一身&#…

Docker搭建私有仓库harbor(docker 镜像仓库搭建)

Harbor介绍 Docker容器应用的开发和运行离不开可靠的镜像管理&#xff0c;虽然Docker官方也提供了公共的镜像仓库&#xff0c;但是从安全和效率等方面考虑&#xff0c;部署我们私有环境内的Registry也是非常必要的。Harbor是由VMware公司开源的企业级的Docker Registry管理项目…

王春城:如何通过看板实现限制在制品数量的目标?

在制造业中&#xff0c;限制在制品&#xff08;Work-in-Process, WIP&#xff09;数量是提高生产效率、减少浪费并优化资源利用的关键一环。通过实施看板系统&#xff08;Kanban System&#xff09;&#xff0c;企业可以有效地管理生产流程&#xff0c;确保在制品数量在合理范围…

【宝藏系列】模/数转换十大常用滤波算法

【宝藏系列】模/数转换十大常用滤波算法 文章目录 【宝藏系列】模/数转换十大常用滤波算法&#x1f468;‍&#x1f3eb;ADC&#xff08;Analog-to-Digital Converter&#xff0c;模数转换器&#xff09;1️⃣限幅滤波法2️⃣中位值滤波法3️⃣算术平均滤波法4️⃣递推平均滤波…

自养号测评:lazada、shopee商家销量提升的必备武器

在东南亚电商跨境平台中、lazada、shopee是东南亚地区最大的在线购物网站&#xff0c;其目标主要是印地&#xff0c;马来&#xff0c;台湾&#xff0c;菲律宾&#xff0c;新加坡&#xff0c;泰国和越南等用户。而自养号补单作为一种有效的推广手段&#xff0c;正逐渐被越来越多…

了解高防 IP

一、高防 IP 的基本概念 高防 IP 是指拥有强大防御能力的 IP 地址。它主要通过将攻击流量引流到高防机房进行清洗和过滤&#xff0c;再将正常的流量回注到源站&#xff0c;从而保障源站服务器的稳定运行。 二、高防 IP 的工作原理 当用户的服务器遭受 DDoS 攻击时&#xff0…

shell脚本及判断语句

一、shell相关概念 1、shell概念 Shell 是一个命令行解释器&#xff0c;它提供了一个用户与操作系统进行交互的界面。通过 Shell&#xff0c;用户可以输入命令来执行程序、管理系统资源&#xff08;如文件和目录&#xff09;、以及执行各种系统级的任务。Shell 是大多数类 Un…

从食堂采购系统源码到成品:打造供应链采购管理平台实战详解

本篇文章&#xff0c;笔者将详细介绍如何从食堂采购系统的源码开始&#xff0c;逐步打造一个完备的供应链采购管理平台&#xff0c;帮助企业实现采购流程的智能化和高效化。 一、需求分析与规划 一般来说&#xff0c;食堂采购系统需要具备以下基本功能&#xff1a; 1.供应商…

原生PHP/JS自主开发的交友内核框架婚恋交友系统V10

本文来自&#xff1a;婚恋交友系统V10 - 源码1688 应用介绍 原生PHP/JS自主开发的交友内核框架&#xff0c;极高性能、无捆绑、自主权、无流水扣点、独立全开源 01脱单盲盒&#xff1a;脱单盲盒类似于漂流瓶&#xff0c;先将自己《投放》到盲盒中&#xff0c;另一伴有缘将您取…

文件上传总结

一、原理 通过界面上的上传功能上传了一个可执行的脚本文件&#xff0c;而WEB端的系统并未对其进行检测或者检测的逻辑做的不够好&#xff0c;使得恶意用户可以通过文件中上传的一句话木马获得操控权 二、绕过方法 1>前端绕过 1.删除前端校验函数 checkFile() 2.禁用js…

Python小工具——监听某网站的数据变化并进行邮件通知

目录 一、需求描述 二、解析 三、实例代码 一、需求描述 监听自考网2024年广东省6月份的毕业生学历注册进度&#xff0c;这是网址&#xff1a;https://www.chsi.com.cn/xlcx/count_zk.jsp&#xff0c; 如上图所示&#xff0c;我们想知道这个红色的空格啥时候被填满&#xf…

如何选择财税RPA解决方案

随着大数据、物联网、人工智能以及RPA等新兴技术的迅猛发展&#xff0c;每个企业都面临着巨大的行业和技术挑战。财务作为企业运营管理的核心&#xff0c;其数字化转型成为众多企业提升管理效能和实现高质量发展的先行路径。随着RPA技术应用在财务领域的不断深入&#xff0c;越…

WireShark 更改界面主题

背景 Windows 是黑色主题 安装 WireShark 后&#xff0c;WireShark 界面也是黑色主题 预期 想要将 WireShark 界面更改为白色主题 操作 启动 wireshark 时添加 -platform windows:darkmode0 参数 <Wireshark.exe 路径> -platform windows:darkmode0 例&#xff1a;…

基于PSO粒子群优化的GroupCNN分组卷积网络时间序列预测算法matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1 粒子群优化算法&#xff08;PSO&#xff09; 4.2 分组卷积神经网络&#xff08;GroupCNN&#xff09; 4.3 PSO优化GroupCNN 5.算法完整程序工程 1.算法运行效果图预览 (完整程序运行…

数据结构篇4—递归实现二叉树基础结构

文章目录 前言&#x1f6a9;1、树&#xff1f;2、树的相关概念3、树的结构表示4、二叉树&#x1f680;、概念和结构&#x1f381;、特殊二叉树 5、二叉树常用性质6、二叉树的存储结构&#x1f9e9;、顺序存储结构&#x1f3a8;、链式存储结构 7、二叉树顺序结构的实现----堆8、…

前端开发知识(三)-javascript(对象)

一、JS对象 包括JS已经定义的对象&#xff0c;如&#xff0c;Array,Sting &#xff0c;DOM&#xff0c;BOM等&#xff0c;其中&#xff0c;JSON是用户自定义对象&#xff08;除对象外&#xff0c;还有文本&#xff09;&#xff0c;其他是JS定义 1.Array&#xff1a;数组 数…

React最新版本 18

截至当前时间&#xff08;2024年07月24日&#xff09;&#xff0c;React 的最新版本是 18.2.0。这个版本在 2022 年 3 月 29 日由 React 团队正式发布&#xff0c;主要着眼于解决 React 应用在性能、稳定性、开发体验等方面的问题。 React 18 是 React 的一个重要版本&#xf…

算法导论 总结索引 | 第五部分 第二十章:van Emde Boas树

1、一些支持优先队列操作的 数据结构,如第6章的二叉堆、第13章的红黑树 和 第19章的斐波那契堆。在这几种数据结构中, 不论是最好情况 还是 摊还情况, 至少有一项重要操作 只需要 O(n lgn) 时间 由于这些数据结构 都是基于关键字比较 决定的&#xff0c;因此, 8.1节中的下界 Ω…