Java的集合框架总结

news2024/11/27 17:58:43

Map接口和Collection接口是所有集合框架的父接口:

  1. Collection接口的子接口包括:Set接口和List接口

  1. Map接口的实现类主要有:HashMap、TreeMap、Hashtable、ConcurrentHashMap以及Properties等

  1. Set接口的实现类主要有:HashSet、TreeSet、LinkedHashSet等

  1. List接口的实现类主要有:ArrayList、LinkedList、Stack以及Vector等

List接口及其实现

ArrayList

  • 基于动态数组实现

  • 允许快速随机访问元素

  • 插入和删除操作可能需要移动其他元素,因此在中间位置插入和删除的时间复杂度为O(n)

  • 非同步,不是线程安全的,可以使用使用Collections.synchronizedList(new ArrayList<>()) 创建一个同步的 ArrayList

LinkedList
  • 基于双向链表实现

  • 允许快速插入删除元素

  • 不能快速随机访问,访问元素的时间复杂度为O(n)

  • 非同步,不是线程安全的。可以使用 Collections.synchronizedList(new LinkedList<>()) 创建一个同步的 LinkedList

  • 可以用做队列或双向队列

    依赖于两个节点(一个头节点一个尾节点)

    常用方法

    添加

    add(E e):在链表后添加一个元素; 通用方法 addFirst(E e):在链表头部插入一个元素; 特有方法 addLast(E e):在链表尾部添加一个元素; 特有方法

    删除

    removeFirst(E e):删除头,获取元素并删除; 特有方法 removeLast(E e):删除尾; 特有方法

    查看

    getFirst():获取第一个元素; 特有方法 getLast():获取最后一个元素; 特有方法

Stack
  • 基于Vector实现,代表了后进先出()

  • 是同步的,是线程安全的

  • push入栈、peek查看栈顶元素、pop出栈、empty是否为空、size()获取数目

Vector
  • 基于动态数组实现,类似于ArrayList

  • 同步的,是线程安全的

  • 是需要线程安全的动态数组是使用,但是通常使用ArrayListCollections.syschronizedList来代替

Set接口及其实现类

set其实一直在用map那一套

 public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable {
     private transient HashMap<E,Object> map;
     private static final Object PRESENT = new Object();
 ​
     public HashSet() {
         map = new HashMap<>();
     }
 ​
     public boolean add(E e) {
         return map.put(e, PRESENT) == null;
     }
 ​
     // Other methods delegate to the underlying map
 }
 ​
HashSet
  • 元素存储HashSet 将元素添加到底层的 HashMap 中,键是元素的哈希码,值是一个占位符对象。HashSet的实现是基于HashMap的,他使用HashMap来存储元素,在HashSet中,元素被当作HashMap的键,而HashMap的值则是一个固定的占位符,通常是 PRESENT 对象(一个静态内部类对象)。这样做是为了节省空间,因为 HashSet 只关心元素的唯一性,不需要存储额外的值。

  • 去重HashSet 使用了 HashMap 键的唯一性来确保 HashSet 中不会有重复的元素。

  • 不保证集合的迭代顺序,顺序可能会随时间变化。

  • 方法委派HashSet 的许多方法(如 addremovecontains 等)都是通过调用底层 HashMap 的对应方法来实现的。

  • 性能:由于 HashSet 是基于 HashMap 实现的,所以其性能与 HashMap 类似,添加、删除和查找操作的平均时间复杂度为 O(1)。

  • 允许存储null值。

LinkedHashSet

LinkedHashSetHashSet 的一个子类,它继承了 HashSet 的特性,并且保持了元素的插入顺序。下面是关于 LinkedHashSet 的一些关键点:

  • 继承自HashSet并且使用链表维护元素的插入顺序

  • 保证迭代顺序与插入顺序一致

  • 允许存储null值。

  • 插入、删除、和查找操作的时间复杂度为O(1)。

实现细节

  • 当你向 LinkedHashSet 添加元素时,它会使用 LinkedHashMap 来存储元素。

  • 每个元素被当作 LinkedHashMap 的键,值则是一个固定的占位符对象。

  • LinkedHashSet 通过委派 LinkedHashMap 来实现其 addremovecontains 等方法。

TreeSet

TreeSet 是 Java 中实现 SortedSet 接口的一个集合类,它基于 TreeMap 实现,并且元素是按照自然顺序(或通过提供的比较器)排序的。以下是 TreeSet 的一些关键点:

  • 基于红黑树(自平衡二叉搜索树)实现

  • 元素是有序的,元素按照自然顺序(或通过Comparator指定的顺序)排

  • 不允许存储null值(会抛出NullPointerException

  • 插入、删除、和查找操作的时间复杂度为O(log n)。

Map接口及其实现类
HashMap

  • 基于哈希表实现,调用put方法值,首先将k,v封装到Node对象中,然后底层会调用k的hashCode()方法得到hash值。根据这个值来决定到底该插入数组下标的哪个位置,如果有两个值的哈希值一样,就会调用equals方法进行比较,如果哈希值一样但是不相等,就会形成链表插入 ,如果相等那么相等的这个节点的value将会被覆盖。如果没有元素占用位置,就直接放入即可。(通过数组加链表形式)

  • 在Java8对hashmap进行了优化,如果相同哈希值,链表的长度超过8,就从链表转换成红黑树。第一次添加元素的时候,默认初期长度为16,当往map中继续添加元素的时候,通过hash值跟数组长度取“与”来决定放在数组的哪个位置,如果出现放在同一个位置的时候,优先以链表的形式存放,在同一个位置的个数又达到了8个(代码是>=7,从0开始,及第8个开始判断是否转化成红黑树),如果数组的长度还小于64的时候,则会扩容数组。如果数组的长度大于等于64的话,才会将该节点的链表转换成树。在扩容完成之后,如果某个节点的是树,同时现在该节点的个数又小于等于6个了,则会将该树转为链表(只有当数据量大于64才会有红黑树+链表)

  • HashMap内部结构是数组(Node[] table)和链表结合组成的复合结构,数组被分成一个个桶(bucket)或槽,通过哈希值决定键值对在这个数组的寻址;哈希值相同的键值对,则以链表形式存储。当链表大小超过阈值(TREEIFY_THRESHOLD = 8)时,链表就会被改造成树形结构。(查询效率变高)

  • Java8不再像Java7中那样需要满足两个条件,Java8中扩容只需要满足一个条件:当前存放新值(注意不是替换已有元素位置时)的时候已有元素的个数大于等于阈值(已有元素等于阈值,下一个存放后必然触发扩容机制)且扩容发生在存放后,即是数据存放后(先存放后扩容),判断当前存入对象的个数,如果大于阈值则进行扩容。

  • 允许存储null键和null值。

  • 插入、删除、和查找操作的时间复杂度为O(1)。

  • 非同步,不是线程安全的。

LinkedHashMap

  • 通过hashmap跟双向链表实现,可以确保按照插入顺序迭代链表

  • 遍历性能: 与普通的HashMap相比,在迭代LinkedHashMap时,性能更加稳定。因为它不需要遍历整个桶,而是按照链表顺序遍历元素。

  • 实现方式: 在内部实现上,LinkedHashMap在每个条目中保留了前一个和后一个条目的引用,以实现双向链表。这使得在插入、删除和遍历元素时的性能表现良好。

TreeMap

  • 基于红黑树实现

  • 键按照自然顺序(或通过Comparator指定的顺序)排序。

  • 不允许存储Null键

  • 插入、删除、和查找操作的时间复杂度为O(log n)。

Hashtable
  • 也是使用哈希表还有链表实现

  • 同步的,是线程安全的。

  • 不允许存储null键和null值。

  • 插入、删除、和查找操作的时间复杂度为O(1)。

Properties
  • 继承自Hashtable,表示一个持久化的属性集。

  • 每个键及其对应值都是一个字符串。

  • 常用于读取和写入配置文件。

ConcurrentHashMap
  • 不允许存储null键和null值。

  • 插入、删除、和查找操作的时间复杂度为O(1)。

  • 在多线程环境下,使用HashMap进行put操作时存在丢失数据的情况,为了避免这种bug的隐患,强烈建议使用ConcurrentHashMap代替HashMap。

  • HashTable是一个线程安全的类,它使用synchronized来锁住整张Hash表来实现线程安全,即每次锁住整张表让线程独占,相当于所有线程进行读写时都去竞争一把锁,导致效率非常低下。ConcurrentHashMap可以做到读取数据不加锁,并且其内部的结构可以让其在进行写操作的时候能够将锁的粒度保持地尽量地小,允许多个修改操作并发进行,其关键在于使用了锁分段技术。它使用了多个锁来控制对hash表的不同部分进行的修改。

分段锁(Segment Locking)机制

早期版本的ConcurrentHashMap(Java 7及之前)使用分段锁机制,具体如下:

  • ConcurrentHashMap内部将整个哈希表分为多个段(Segment),每个段都是一个独立的哈希表,并拥有自己的锁。

  • 这种机制允许多个线程同时访问不同段的元素,从而提高并发度。

  • ConcurrentHashMap 为了提高本身的并发能力,在内部采用了一个叫做 Segment 的结构,一个 Segment 其实就是一个类 Hash Table 的结构,Segment 内部维护了一个链表数组,我们用下面这一幅图来看下 ConcurrentHashMap 的内部结构,从下面的结构我们可以了解到,ConcurrentHashMap 定位一个元素的过程需要进行两次Hash操作,第一次 Hash 定位到 Segment,第二次 Hash 定位到元素所在的链表的头部,因此,这一种结构的带来的副作用是 Hash 的过程要比普通的 HashMap 要长,但是带来的好处是写操作的时候可以只对元素所在的 Segment 进行操作即可,不会影响到其他的 Segment,这样,在最理想的情况下,ConcurrentHashMap 可以最高同时支持 Segment 数量大小的写操作(刚好这些写操作都非常平均地分布在所有的 Segment上),所以,通过这一种结构,ConcurrentHashMap 的并发能力可以大大的提高。

CAS操作和分段锁

在Java 8中,ConcurrentHashMap使用了一种新的机制,结合了CAS(Compare-And-Swap)操作和细粒度的分段锁。

  • 使用CAS操作来保证对单个节点的原子性操作,减少锁的使用。

  • 在插入、删除和更新操作中,如果CAS操作失败(即另一个线程同时修改了相同的位置),则退而使用锁进行操作。

  • 在Java 8中,ConcurrentHashMap取消了分段锁的概念,直接在哈希桶(bucket)级别进行锁定,使用CAS操作和synchronized块来保证并发安全。这使得数据结构更加简单,且操作更加直观。

  • 为了优化哈希冲突情况下的查找性能,Java 8引入了红黑树。当链表的长度超过一定阈值(默认是8)时,链表会转换为红黑树。这样,在高冲突情况下,查找操作的时间复杂度从O(n)降到了O(log n),极大地提高了性能。

  • CAS 操作通过比较当前值与预期值,如果两者相等则更新为新值,否则重试该操作。

数据结构

ConcurrentHashMap在Java 8中的内部数据结构有以下几个关键组成部分:

  • 数组(Node<K,V>[] table):哈希表的核心数组,存储链表或红黑树的头节点。

  • 链表和红黑树:数组中的每个桶(bucket)最初是一个链表。当链表长度超过阈值(8)时,链表会转换为红黑树,以提高查询效率。

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

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

相关文章

【数据结构】二叉树:一场关于节点与遍历的艺术之旅

专栏引入 哈喽大家好&#xff0c;我是野生的编程萌新&#xff0c;首先感谢大家的观看。数据结构的学习者大多有这样的想法&#xff1a;数据结构很重要&#xff0c;一定要学好&#xff0c;但数据结构比较抽象&#xff0c;有些算法理解起来很困难&#xff0c;学的很累。我想让大家…

数据结构之链表的经典笔试题

找往期文章包括但不限于本期文章中不懂的知识点&#xff1a; 个人主页&#xff1a;我要学编程(ಥ_ಥ)-CSDN博客 所属专栏&#xff1a;数据结构&#xff08;Java版&#xff09; 目录 203. 移除链表元素 206. 反转链表 876. 链表的中间节点 面试题 02.02. 返回倒数第k个节点 …

零基础非科班也能掌握的C语言知识22 预处理详解(完结)

预处理详解 1.预处理符号2.#define 定义常量3.#define 定义宏4.带有副作用的宏参数5.宏替换的规则6.宏函数的对比6.1 例子6.1 .16.1.26.1.3 7.命名约定8.undefin9.命令行定义(博主没办法演示)10.条件编译11.头文件的包含11.1本地文件11.2库文件的包含11.3 嵌套文件的包含 12.其…

软件安全测评有哪些测试流程?第三方检测机构进行安全测评的好处

在今天的高科技时代&#xff0c;软件产品已经成为人们生活和工作的重要组成部分。然而&#xff0c;与其普及和深入应用的&#xff0c;软件安全问题也日益凸显。 为了保障软件产品在使用过程中的安全性&#xff0c;进行安全测评是必不可少的。安全测评可以全面评估软件系统的安…

GPT-4o多模态大模型的架构设计

GPT-4o&#xff1a;大模型风向&#xff0c;OpenAI大更新 OpenAI震撼发布两大更新&#xff01;桌面版APP与全新UI的ChatGPT上线&#xff0c;简化用户操作&#xff0c;体验更自然。同时&#xff0c;全能模型GPT-4o惊艳亮相&#xff0c;跨模态即时响应&#xff0c;性能卓越且性价比…

Java集合自测题

文章目录 一、说说 List , Set , Map 三者的区别&#xff1f;二、List , Set , Map 在 Java 中分别由哪些对应的实现类&#xff1f;底层的数据结构&#xff1f;三、有哪些集合是线程不安全的&#xff1f;怎么解决呢&#xff1f;四、HashMap 查询&#xff0c;删除的时间复杂度五…

k8s中的pod域名解析失败定位案例

问题描述 我在k8s中启动了一个Host网络模式的pod&#xff0c;这个pod的域名解析失败了。 定位步骤 敲kubectl exec -it [pod_name] -- bash进入pod后台&#xff0c;查看/etc/resolv.conf&#xff0c;发现nameserver配的有问题。这里我预期的nameserver应该使用宿主机的&…

【Linux】线程(一)

谈论之前需要先谈论一些线程的背景知识 其中就有进程地址空间&#xff0c;又是这个让我们又爱又恨的东西 目录 背景知识&#xff1a;地址空间&#xff1a; 背景知识&#xff1a; 地址空间&#xff1a; 说在前边&#xff0c;OS通常分为4个核心模块&#xff1a;执行流管理&…

【qt】绘图

绘图 一.画家二.绘图事件三.坐标体系四.画笔1.setColor2.setWidth3.setStyle4.setCapStyle5.setJoinStyle6.给画家配置笔 五.画刷1.setColor2.setStyle3.给画家设置刷子 六.用到的类汇总1.QRect 矩形2.QPoint 点3.QImage 图片4.QPixmap 图片5.QLine 线6.QPainterPath 路径 七.开…

如何关闭端口被占用的进程

如何关闭端口被占用的进程 操作步骤一、打开命令提示符&#xff08;Command Prompt&#xff09;二、查看占用端口的进程三、kill杀死占用端口的进程 操作步骤 一、打开命令提示符&#xff08;Command Prompt&#xff09; 使用 win R 打开命令行模式 然后在命令行窗口输入下…

【LLM Agent 长文本】Chain-of-Agents与Qwen-Agent引领智能体长文本处理革命

前言 大模型在处理长文本上下文任务时主要存在以下两个问题&#xff1a; 输入长度减少&#xff1a;RAG的方法可以减少输入长度&#xff0c;但这可能导致所需信息的部分丢失&#xff0c;影响任务解决性能。扩展LLMs的上下文长度&#xff1a;通过微调的方式来扩展LLMs的上下文窗…

基于DenseNet网络实现Cifar-10数据集分类

目录 1.作者介绍2.Cifar-10数据集介绍3.Densenet网络模型3.1网络背景3.2网络结构3.2.1Dense Block3.2.2Bottleneck层3.2.3Transition层3.2.4压缩 4.代码实现4.1数据加载4.2建立 DenseNet 网络模型4.3模型训练4.4训练代码4.5测试代码 参考链接 1.作者介绍 吴思雨&#xff0c;女…

搭建自己的多平台镜像站

# 1. 拉取代码 $ git clone https://github.com/wzshiming/crproxy.git $ cd crproxy/examples/default# 2. 修改网关域名 使用vim编辑start.sh文件&#xff0c;将第五行的gateway变量值修改为你自己设定的域名。 原&#xff1a;gatewaycr.zsm.io 修改为&#xff1a;gatewayXS…

go语言 | 快速生成数据库表的 model 和 queryset

就是生成 model 目录的 xxx.go 和 xxx_gen.go 文件 使用的工具&#xff1a; 快速生成 model&#xff1a;gentool&#xff1a;https://github.com/go-gorm/gen/tree/master/tools/gentool 根据 model 生成 queryset&#xff1a;go-queryset&#xff1a;https://github.com/jirfa…

layuimini框架实现点击菜单栏回到起始页

在layui页面中&#xff0c;如果使用了 window.location.href""进行了页面跳转&#xff0c;再点击菜单栏是不会显示起始页&#xff0c;而是跳转后的页面&#xff0c; 解决&#xff1a; 在miniTab.js文件中找到&#xff1a;listen方法 将其中修改为&#xff1a; if …

全球首创4090推理!昆仑万维开源Skywork-MoE模型

昆仑万维近期宣布开源了其2千亿参数规模的稀疏大模型Skywork-MoE。这个模型是基于他们之前开源的Skywork-13B模型中间checkpoint扩展而来的&#xff0c;并且宣称是首个完整应用MoE Upcycling技术的开源千亿MoE大模型。此外&#xff0c;它也是首个支持使用单台RTX 4090服务器&am…

MyBatisPlus代码生成器(交互式)快速指南

引言 本片文章是对代码生成器(交互)快速配置使用流程&#xff0c;更多配置方法可查看官方文档&#xff1a; 代码生成器配置官网 如有疑问欢迎评论区交流&#xff01; 文章目录 引言演示效果图引入相关依赖创建代码生成器对象引入Freemarker模板引擎依赖支持的模板引擎 MyBat…

Day 20:2806. 取整购买后的账户余额

Leetcode 2806. 取整购买后的账户余额 一开始&#xff0c;你的银行账户里有 100 块钱。 给你一个整数purchaseAmount &#xff0c;它表示你在一次购买中愿意支出的金额。 在一个商店里&#xff0c;你进行一次购买&#xff0c;实际支出的金额会向 最近 的 10 的 倍数 取整。换句…

【QT5】<总览二> QT信号槽、对象树及常用函数

文章目录 前言 一、QT信号与槽 1. 信号槽连接模型 2. 信号槽介绍 3. 自定义信号槽 二、QT的对象树 三、添加资源文件 四、样式表的使用 五、QSS文件的使用 六、常用函数与宏 前言 承接【QT5】&#xff1c;总览一&#xff1e; QT环境搭建、快捷键及编程规范。若存在版…

Python使用tkinter库设置背景图片、label显示位置和label设置显示图片

tkinter 设置背景图片 label显示位置 label设置显示图片 from tkinter import * import tkinter as tk from PIL import ImageTk from PIL import Imagedef get_img(filename, width, height):im Image.open(filename).resize((width, height))im ImageTk.PhotoImage(im)…