当前位置: 首页 > article >正文

Java集合的底层原理

目录

Collection

Arraylist

HashSet

介绍

哈希值

哈希表的基本概念

HashSet 的内部实现

HashMap

哈希碰撞的处理

总结

TreeSet

特点

红黑树的特性

红黑规则

TreeSet 的内部实现

1. 存储结构

2. 添加元素(重点)

3. 查找元素

4. 删除元素

总结

几个问题思考


Collection

  1. List
    • Arraylist: Object数组
    • Vector: Object数组
    • LinkedList: 双向链表
  1. Set
    • HashSet(无序,唯一):基于 HashMap 实现的底层采用 HashMap 来保存元素
    • LinkedHashSet: LinkedHashSet 继承于 HashSet,并且其内部是通过 LinkedHashMap 来实现的。有点类似于我们之前说的LinkedHashMap 其内部是基于 Hashmap 实现一样,不过还是有一点点区别的。
    • TreeSet(有序,唯一): 红黑树(自平衡的排序二叉树。)
  • Map
  • HashMap: JDK1.8之前HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的(“拉链法”解决冲突).JDK1.8以后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间
  • LinkedHashMap(存取有序):LinkedHashMap 继承自 HashMap,所以它的底层仍然是基于拉链式散列结构即由数组和链表或红黑树组成。另外,LinkedHashMap 在上面结构的基础上,增加了一条双向链表,使得上面的结构可以保持键值对的插入顺序。同时通过对链表进行相应的操作,实现了访问顺序相关逻辑。

  • HashTable: 数组+链表组成的,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的
  • TreeMap(可排序): 红黑树(自平衡的排序二叉树)

存取有序:存和取的元素顺序相同。

Arraylist

注:扩容时会创建一个新数组,会先将原数组的数据拷贝到新数组当中。。。

HashSet

介绍

HashSet 的底层实现主要依赖于 HashMap。在 Java 中,HashSet 的实现是通过内部维护一个 HashMap 实例来完成的,而 HashMap 则是基于哈希表的数据结构。哈希表是一种使用哈希函数来快速访问数据的数据结构,它通过把键映射到表中一个位置来访问记录,以加快查找的速度。

哈希值

重写自定义类的 equals 方法以及 hashCode 方法:

举例(Student 类):

哈希表的基本概念

  1. 哈希函数:哈希表首先会定义一个哈希函数,用于将键转换为数组索引。在 Java 的 HashSet 中,这个哈希函数会使用hashCode() 方法来生成一个哈希码。
  2. 数组 + 链表/红黑树:哈希表通常由一个数组组成,数组的每个槽位(slot)或称为桶(bucket),可以存储一个或多个元素。如果两个不同的键通过哈希函数产生了相同的索引(哈希碰撞),那么这些键将以链表的形式存储在同一个桶中。在 Java 8 之后,如果链表长度超过一定阈值(默认为8),链表会转换为红黑树,以减少查找时间。

HashSet 的内部实现

注:

当数组存储的数据达到 (0.75*数组长度) 时,会对数组进行2倍扩容。

链表长度大于8而且数组长度大于等于64 时:链表自动转成红黑树。

如果集合中存储的是自定义对象,必须要重写hashcodeequals方法!!!

  1. 存储结构HashSet 内部维护了一个 HashMap 实例,这个 HashMap 的每个键值对中的值都是一个固定的对象(在 HashSet 的实现中,这个对象并不重要,通常是一个名为 PRESENT 的静态 final 对象)。
  2. 添加元素:当向 HashSet 添加一个元素时,首先会调用这个元素的 hashCode() 方法来计算它的哈希码(哈希值),然后根据哈希码找到对应的桶。如果这个桶中没有元素,或者元素与要添加的元素相等(通过 equals() 方法比较),则将元素添加到桶中。如果发生哈希碰撞,新元素将被添加到链表的末尾,或者添加到红黑树中。
  3. 查找元素:当要查找一个元素时,HashSet 会使用同样的哈希函数来计算元素的哈希码,然后到对应的桶中查找。如果找到了相等的元素,则返回 true。如果桶中有多个元素(因为哈希碰撞),那么会使用 equals() 方法来逐个比较链表或红黑树中的元素。
  4. 删除元素:删除元素的过程与查找类似,首先找到元素所在的桶,然后从链表或红黑树中删除元素。

HashMap

HashSet 内部本质是维护了一个 HashMap 实例,所以它们的底层基本相同,

唯一不同的地方:

当用 equals 方法比较属性值之后,如果一样的话,HashSet 底层是不存新元素,而 HashMap 的底层是覆盖(新加的元素覆盖老的元素)。

其实 HashSet 的底层也是覆盖,只是因为它不存和覆盖的效果一样,而 HashMap 键一样但是值可能不一样,就会出现覆盖的效果。

哈希碰撞的处理

哈希碰撞是哈希表中的一个重要概念,当两个不同的键产生了相同的哈希码时,就会发生哈希碰撞。Java 的 HashSet 通过链表或红黑树来解决哈希碰撞:

  • 链表:在发生碰撞的情况下,新元素将被添加到链表的末尾。如果链表变得过长,那么它的性能会下降,因为查找时间会变长。
  • 红黑树:在 Java 8 中,如果一个桶中的链表长度超过阈值(默认为8),链表会被转换成红黑树。红黑树是一种自平衡的二叉搜索树,它能够保持树的平衡,从而保证查找、插入和删除的最坏情况时间复杂度为 O(log n)。

总结

HashSet 的底层哈希表实现提供了高效的添加、删除和查找操作。它的性能取决于哈希函数的质量、哈希碰撞的处理以及负载因子的设置。负载因子决定了哈希表什么时候进行扩容,默认值为 0.75,这意味着当哈希表中的元素数量达到数组大小的 75% 时,哈希表会进行扩容操作,以减少哈希碰撞的几率。

TreeSet

TreeSet 是 Java 中 SortedSet 接口的一个实现,它基于红黑树 数据结构来存储元素。红黑树是一种自平衡的二叉搜索树,它能够保持树的平衡,从而保证最坏情况下的查找、插入和删除操作的时间复杂度为 O(log n)。下面是 TreeSet 底层实现的详细介绍:

特点

可排序,不重复,无索引

红黑树的特性

红黑树具有以下五个主要特性:

  1. 每个节点包含一个颜色属性:节点可以是红色或黑色。
  2. 根节点是黑色:红黑树的根节点必须是黑色。
  3. 红色节点不能连续:红黑树中不能有两个连续的红色节点,即红色节点的子节点和父节点不能是红色。
  4. 从任一节点到其每个叶子节点的所有路径都包含相同数量的黑色节点:这意味着从根节点到叶子节点的所有路径上的黑色节点数目是相同的,这个属性称为“黑色完美性”。
  5. 新插入的节点是红色:新插入的节点默认为红色,以保持其他红黑树性质的稳定性。

红黑规则

TreeSet 的内部实现

1. 存储结构

TreeSet 内部维护了一个 NavigableMap 实例,默认情况下这个实例是 TreeMap。TreeMap 的底层实现就是红黑树,它存储了排序后的键值对,而 TreeSet 实际上只是使用了 TreeMap 的键来存储元素,值则是一个固定的对象(通常是一个静态的 final 对象)

2. 添加元素(重点)

当向 TreeSet 添加一个元素时,TreeMap 会根据元素的比较结果(自然排序或者比较器排序)来确定元素的存储位置。如果元素(自定义的类)实现了 Comparable 接口,那么会使用这个接口的方法来比较元素。如果没有实现 Comparable,那么在创建 TreeSet 集合时必须自定义Comparator比较器对象来定义元素的排序规则。添加元素时,红黑树会进行颜色变更和旋转操作,以保持树的平衡。

也可以分为以下两种情况:

Java写好的类
//1.自然排序/默认排序
//Integer Double  默认情况下按照升序排列(Java的内部源码)
//String  按照字母在ASCll码表中对应的数字升序排列
//2.比较器排序
//创建集合时传递Comparator比较器对象,指定比较规则
自定义的类
//1.默认排序/自然排序
//自定义类实现Comparable接口,再重写compareTo方法(用于指定排序规则)
//2.比较器排序
//创建集合时传递Comparator比较器对象,指定比较规则

代码举例:

如:(比较器排序)

import java.util.Comparator;
import java.util.TreeSet;public class TreeSetExample {public static void main(String[] args) {//使用匿名内部类创建 TreeSet,并传入自定义的比较器TreeSet<Integer> ts1 = new TreeSet<>(new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {//o1:当前要添加的元素//o2:表示已经在红黑树中存在的元素return o2 - o1;//实现降序功能//o2 - o1 可能导致整数溢出,//return Integer.compare(o2, o1); //这个是更安全的降序比较}});//使用Lambda表达式创建 TreeSet,并传入自定义的比较器TreeSet<Integer> ts2 = new TreeSet<>((o1, o2) -> o2 - o1);// 向 TreeSet 中添加元素ts1.add("Apple");ts1.add("Banana");ts1.add("Cherry");ts1.add("Date");// 输出 TreeSet 中的元素System.out.println(ts1);}
}

如:(自然排序、默认排序)

public class Student2 implements Comparable<Student2> {private String name;private int age;public Student2() {}public Student2(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String toString() {return "Student2{name = " + name + ", age = " + age + "}";}//自定义类实现Comparable接口,再重写compareTo方法(用于指定排序规则)@Overridepublic int compareTo(Student2 o) {//this:当前要添加的元素//o:表示已经在红黑树中存在的元素//返回值://负数:表示当前要添加的元素是小的,存左边//正数:表示当前要添加的元素是大的,存右边//0:覆盖int i = this.getAge() - o.getAge();//按照年龄升序排序//年龄一样按照姓名的字母排序i = i == 0 ? this.getName().compareTo(o.getName()) : i;return i;}
}

自然排序和比较器排序的合理使用:

  • 使用自然排序:当集合中的元素类型已经实现了 Comparable 接口,并且你希望使用这个排序规则时,可以选择自然排序。这是最简单的情况,因为你不需要提供额外的比较器。
  • 使用比较器排序:当集合中的元素类型没有实现 Comparable 接口,或者你希望使用不同于自然顺序的排序规则时,应该提供一个自定义的 Comparator。这允许你根据特定的业务逻辑或者需求来定义排序规则。
  • 注意事项:在使用 TreeSet 时,确保添加到集合中的所有元素都是可比较的,无论是通过自然排序还是通过比较器排序。如果元素不可比较,或者在比较时抛出 ClassCastException,那么在添加元素时会抛出异常。
  • 性能考虑比较器排序可能会稍微降低性能,因为它需要使用额外的 Comparator 对象来进行比较。如果性能是关键考虑因素,并且可以接受自然排序,那么应该优先使用自然排序。

3. 查找元素

查找元素时,TreeSet 会使用红黑树的搜索性质来定位元素。由于树是平衡的,所以查找操作的时间复杂度为 O(log n)。

4. 删除元素

删除元素时,TreeSet 会先找到要删除的节点,然后根据不同情况执行删除操作,并可能需要进行颜色变更和旋转操作,以保持红黑树的性质。

总结

TreeSet 的底层红黑树实现提供了有序的集合存储,并且保证了高效的添加、删除和查找操作。由于红黑树的自平衡特性,TreeSet 能够在元素动态变化的情况下保持良好的性能。与基于哈希表的 HashSet 相比,TreeSet 在元素顺序和排序功能上提供了更多的支持,但可能在某些操作上稍微慢一些,尤其是在最坏情况下的性能。

几个问题思考

相关文章:

Java集合的底层原理

目录 Collection Arraylist HashSet 介绍 哈希值 哈希表的基本概念 HashSet 的内部实现 HashMap 哈希碰撞的处理 总结 TreeSet 特点 红黑树的特性 红黑规则 TreeSet 的内部实现 1. 存储结构 2. 添加元素&#xff08;重点&#xff09; 3. 查找元素 4. 删除元…...

SPI驱动(九) -- SPI_Master驱动程序

文章目录 参考资料&#xff1a;一、SPI传输概述二、SPI传输的两种方法2.1 旧方法2.2 新方法 参考资料&#xff1a; 参考资料&#xff1a; 参考内核源码: drivers\spi\spi.c 一、SPI传输概述 SPI控制器的作用是发起与它下面挂接的SPI设备之间的数据传输&#xff0c;那么控制…...

MySQL常用函数详解及SQL代码示例

MySQL常用函数详解及SQL代码示例 引言当前日期和时间函数字符串函数数学函数聚合函数结论 引言 MySQL作为一种广泛使用的关系型数据库管理系统&#xff0c;提供了丰富的内置函数来简化数据查询、处理和转换。掌握这些函数可以大大提高数据库操作的效率和准确性。本文将详细介绍…...

Linux 进程的创建、终止、等待与程序替换函数 保姆级讲解

目录 一、 进程创建 fork函数 二、进程的终止&#xff1a; 1. 想明白&#xff1a;终止是在做什么&#xff1f; 2.进程终止的3种情况&#xff1f; a.退出码是什么&#xff1f;存在原因&#xff1f;为什么int main&#xff08;&#xff09;return 0? b.第三种进程终止的情况…...

大数据(1.1)纽约出租车大数据分析实战:从Hadoop到Azkaban的全链路解析与优化

目录 一、背景与数据价值‌ ‌二、技术选型与组件分工‌ ‌三、数据准备与预处理‌ 四、实战步骤详解‌ ‌1. 数据上传至HDFS ‌2. Hive数据建模与清洗‌ 4‌.2.1 建表语句&#xff08;分区表按年份&#xff09;‌&#xff1a; ‌4‌.2.2 数据清洗&#xff08;剔除无效…...

BSCAN2-1:load design

1. DFT Flow Using Tessent Shell Tessent BoundaryScan 具有一个基本的高层次流程顺序。下图展示了将 Tessent BoundaryScan 插入设计所需的高层次步骤顺序。图中的每个步骤都链接到有关可测试性设计&#xff08;DFT&#xff09;流程的更详细信息&#xff0c;包括示例。 Desi…...

个人学习编程(3-18) leetcode刷题

爬楼梯&#xff1a; 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f; 示例 1&#xff1a; 输入&#xff1a;n 2 输出&#xff1a;2 解释&#xff1a;有两种方法可以爬到楼顶。 1. 1 阶 1 阶 2. 2 …...

【css酷炫效果】纯CSS实现立体旋转立方体

【css酷炫效果】纯CSS实现立体旋转立方体 缘创作背景html结构css样式完整代码效果图 想直接拿走的老板&#xff0c;链接放在这里&#xff1a;https://download.csdn.net/download/u011561335/90492014 缘 创作随缘&#xff0c;不定时更新。 创作背景 刚看到csdn出活动了&am…...

Android Fresco 框架兼容模块源码深度剖析(六)

Android Fresco 框架兼容模块源码深度剖析 一、引言 在 Android 开发的多元环境中&#xff0c;兼容性是衡量一个框架优劣的重要指标。Fresco 作为一款强大的图片加载框架&#xff0c;其兼容模块在确保框架能在不同 Android 版本、不同设备和不同图片格式下稳定运行方面发挥着…...

ABSD基于架构的软件设计

基于架构的设计&#xff08;ABSD&#xff09;Architecture-Based Software Design是一种软件设计方法&#xff0c;强调软件架构设计应该由商业、质量和功能需求共同驱动。这种方法允许设计活动在明确项目总体功能框架的前提下开始&#xff0c;并且需求抽取和分析活动应与设计活…...

LLM中lora的梯度更新策略公式解析

LLM中lora的梯度更新策略公式解析 目录 LLM中lora的梯度更新策略公式解析区别如何使用LoRA代码中的参数更新方式二阶导数(如右侧公式关联的Fisher信息)的作用区别 定义与理论来源: 左公式 F ( w i ) = 1 n...

开源数据仓库全解 — 从原理到实践

&#x1f3af; 一、什么是数据仓库&#xff1f; 数据仓库&#xff08;Data Warehouse&#xff0c;简称 DW&#xff09;是面向分析和决策的专门数据存储系统&#xff0c;旨在整合来自多个源的数据&#xff0c;支持复杂查询和大规模分析任务。 特点包括&#xff1a; 面向主题&…...

Mac下Ollama安装全攻略:开启本地大模型之旅

文章目录 Mac下Ollama安装全攻略&#xff1a;开启本地大模型之旅一、Ollama 是什么功能特点优势应用场景 二、安装前准备&#xff08;一&#xff09;系统要求&#xff08;二&#xff09;硬件要求 三、下载安装包&#xff08;一&#xff09;官网下载&#xff08;二&#xff09;其…...

线程大乱斗:从入门到精通,解锁Java并发编程的终极秘籍

目录 什么是线程&#xff1f; jave创建线程方式有几种&#xff1f; 线程中常用的方法 线程状态 多线程 解决线程安全问题 线程通信 何为并发编程&#xff1f; 并发执行和并行执行 线程的三个主要问题&#xff1a; 1、不可见性&#xff1a; 2、乱序性&#xff1a; …...

Web3游戏行业报告

一&#xff0c;gamefi经济 什么是gamefi GameFi是一个缩写&#xff0c;它结合了游戏和去中心化金融(“DeFi”)这两个术语&#xff0c;关注的是游戏玩法如何在去中心化系统中实现货币化。对于游戏而言&#xff0c;只要开放了交易市场&#xff0c;允许玩家自由买卖&#xff0c;…...

hibernate 自动生成数据库表和java类 字段顺序不一致 这导致添加数据库数据时 异常

hibernate 自动生成的数据库表和java类 字段顺序不一致 这导致该书写方式添加数据库数据时 异常 User user new User( null, username, email, phone, passwordEncoder.encode(password) ); return userRepository.save(user);Hibernate 默认不会保证数据库表字段的顺序与 Ja…...

bbbbb

import java.util.ArrayList; import java.util.List; public class KthPermutation { public static String getPermutation(int n, int k) { // 计算阶乘 int[] factorial new int[n]; factorial[0] 1; for (int i 1; i < n; i) …...

《基于Workspace.java的Launcher3改造:HotSeat区域动态阻断文件夹生成机制》

1. 需求背景与技术挑战 在Android 13系统Launcher3定制化开发中&#xff0c;需实现禁止HotSeat区域创建文件夹的功能。原始逻辑中&#xff0c;当用户拖拽应用图标至HotSeat区域相邻图标时&#xff0c;会触发FolderIcon的实例化。本文将深入分析Launcher3的文件夹创建机制&…...

Cursor在内网环境配置自定义DeepSeek API

关键字 Cursor、DeepSeek、API配置、内网代理、HTTP/2 背景环境 使用Cursor集成环境开发程序。但是我使用公司的内网并不能使用cursor自带的模型&#xff0c;于是我就想使用DeepSeek官方的API服务。 环境&#xff1a;Windows 11系统 解决过程 网络检测 首先进行环境检测&am…...

【数据库】掌握MySQL事务与锁机制-数据一致性的关键

在数据库的世界里&#xff0c;数据就是一切。而确保数据的准确性和一致性&#xff0c;则是数据库系统的核心任务之一。想象一下&#xff0c;如果没有合适的机制&#xff0c;当多个用户同时试图修改同一条数据时&#xff0c;会发生什么&#xff1f; chaos&#xff08;混乱&#…...

在Vue3中使用$router.push方法进行路由跳转时,如何传递多个路径参数?

在 Vue 3 里&#xff0c;你可以借助 $router.push 方法进行路由跳转&#xff0c;同时传递多个路径参数。下面为你详细介绍具体实现方式&#xff1a; 1. 路由配置 首先&#xff0c;要在路由配置中定义好需要的路径参数。示例如下&#xff1a; import { createRouter, createW…...

【初学者】解释器和脚本各是什么?有什么区别与联系?

李升伟 整理 解释器和脚本的定义 1. 解释器&#xff08;Interpreter&#xff09; 定义&#xff1a;解释器是一个程序&#xff0c;负责逐行读取并执行代码。它将源代码翻译成机器能理解的指令&#xff0c;并立即执行。特点&#xff1a; 逐行执行代码。适合交互式编程&#xf…...

Kafka跨集群数据备份与同步:MirrorMaker运用

#作者&#xff1a;张桐瑞 文章目录 前言MirrorMaker是什么运行MirrorMaker各个参数的含义 前言 在大多数情况下&#xff0c;我们会部署一套Kafka集群来支撑业务需求。但在某些特定场景下&#xff0c;可能需要同时运行多个Kafka集群。比如&#xff0c;为了实现灾难恢复&#x…...

AI学习第二天--监督学习 半监督学习 无监督学习

目录 1. 监督学习&#xff08;Supervised Learning&#xff09; 比喻&#xff1a; 技术细节&#xff1a; 形象例子&#xff1a; 2. 无监督学习&#xff08;Unsupervised Learning&#xff09; 比喻&#xff1a; 技术细节&#xff1a; 形象例子&#xff1a; 3. 半监督学…...

设计模式(创建型)-抽象工厂模式

摘要 在软件开发的复杂世界中,设计模式作为解决常见问题的最佳实践方案,一直扮演着至关重要的角色。抽象工厂模式,作为一种强大的创建型设计模式,在处理创建一系列或相关依赖对象的场景时,展现出了独特的优势和灵活性。它通过提供一个创建对象的接口,让开发者能够在不指定…...

linux系统 Ubuntu22.04安装Nvidia驱动,解决4060系列显卡重启黑屏方法

一、禁用Nouveau 1.查看nouveau lsmod | grep nouveau 2.编辑 blacklist.conf sudo gedit /etc/modprobe.d/blacklist.conf 3.在文件最后加入 blacklist nouveau options nouveau modeset0 4.保存并关闭文件 5.更新 sudo update-initramfs -u 6.重启之后&#xff0c;检…...

观察者模式详解:用 Qt 信号与槽机制深入理解

引言 你是否曾遇到这样的需求&#xff1a;一个对象的状态发生变化后&#xff0c;希望通知其他对象进行相应的更新&#xff1f;比如&#xff1a; 新闻订阅系统&#xff1a;当新闻发布后&#xff0c;所有订阅者都会收到通知。股票行情推送&#xff1a;股价变化时&#xff0c;所…...

OSWorld:开启多模态智能体的真实计算机环境革命

OSWorld:开启多模态智能体的真实计算机环境革命 在人工智能技术突飞猛进的今天,多模态智能体正逐步突破实验室的限制,试图融入人类的日常工作场景。然而,如何评估这些智能体在真实计算机环境中处理开放式任务的能力,成为学术界和产业界共同关注的难题。2024年,由xlang-ai…...

LabVIEW烟气速度场实时监测

本项目针对燃煤电站烟气流速实时监测需求&#xff0c;探讨了静电传感器结构与速度场超分辨率重建方法&#xff0c;结合LabVIEW多板卡同步采集与实时处理技术&#xff0c;开发出一个高效的烟气速度场实时监测系统。该系统能够在高温、高尘的复杂工况下稳定运行&#xff0c;提供高…...

电脑管家如何清理内存及垃圾,提升电脑性能

电脑在长时间使用后&#xff0c;常常会变得越来越卡顿&#xff0c;打开程序的速度变慢&#xff0c;甚至响应迟缓。这时&#xff0c;不少用户会选择使用电脑管家来进行内存清理和垃圾清理。那么&#xff0c;电脑管家是如何清理内存的&#xff1f;它又是如何清理垃圾的&#xff1…...