实用指南:Java 集合解析

news/2025/9/18 18:36:05/文章来源:https://www.cnblogs.com/wzzkaifa/p/19099430

一、ArrayList

1. 底层数据结构

ArrayList 的核心是一个Object 类型的数组,名为 elementData。它不是固定大小的,而是随着元素增加动态扩容。

数组初始默认不分配空间(JDK 1.8+),第一次添加元素时才初始化为长度 10。

对象引用(可为 null)。就是数组中每个位置存储的

维护一个 size 变量,表示当前实际存储的元素个数(不是数组长度)。

关键点:数组连续内存 ,随机访问快(O(1)),但插入/删除慢(需移动元素)。

2. 扩容机制

每次调用 add() 方法时,ArrayList 会先检查:当前数组容量是否足够容纳新元素。判断逻辑是:

如果 size + 1 > 当前数组长度,则触发扩容。

例如:数组长度为 10,已存 10 个元素,再 add 第 11 个 → 触发扩容。

扩容方式

  1. 计算新容量

    • 新容量 = 旧容量 + 旧容量右移一位(即 1.5 倍)。
    • 举例:10 → 15,15 → 22
    • 假设 1.5 倍仍不够(比如批量添加大量元素),则直接扩容到所需的最小容量。
  2. 检查最大限制

    • 如果新容量超过 JVM 数组最大长度(Integer.MAX_VALUE - 8),则调用特殊方法处理,可能抛出 OutOfMemoryError。
  3. 创建新数组并复制数据

    • 调用 Arrays.copyOf(),底层使用 System.arraycopy()(native 方法,高效内存复制)。
    • 将旧数组所有元素复制到新数组。
    • elementData 引用指向新数组,旧数组等待 GC 回收。

性能代价:扩容是 O(n) 操作,频繁扩容严重影响性能。

3. 线程安全方面

完全不安全

多线程同时 add 时,两个线程可能读到相同的 size,计算出相同的插入位置,导致一个元素被覆盖。

多线程同时扩容时,可能一个线程刚创建新数组,另一个线程又基于旧数组长度计算新容量,造成数组长度错误或越界异常。

二、LinkedList

1. 底层数据结构

LinkedList 由一个个Node 节点组成双向链表。

每个 Node 包含:当前元素值、指向前一个节点的引用(prev)、指向后一个节点的引用(next)。

LinkedList 对象本身持有两个引用:first(头节点)和 last(尾节点)。

没有容量概念,添加元素就是创建新节点并调整指针。

关键点:链表结构 → 插入/删除快(O(1)),但随机访问慢(O(n))。

2. 扩容机制

没有扩容

每次 add 都是新建一个 Node 对象,然后调整相邻节点的指针。

理论上只受堆内存限制,不会像数组那样得“预留空间”或“复制资料”。

优势:无需担心扩容性能开销。
劣势:每内存不连续,缓存不友好。

3. 线程安全方面

不安全

多线程同时操作头尾指针(first/last)或节点的 next/prev 指针,会导致链表断裂、节点丢失、甚至形成环。

例如:两个线程同时在尾部添加,可能都把新节点的 prev 指向同一个旧尾节点,导致其中一个节点被“跳过”。

三、HashMap(JDK 1.8+)

1. 底层数据结构

HashMap 由一个Node 数组(哈希桶数组)构成,数组每个位置称为一个桶(bucket)。

通过每个桶能够是:

空(null)

一个 Node 对象(无冲突)

一个 Node 链表(哈希冲突)

一个 TreeNode 红黑树(冲突严重时优化)

Node 含有:key 的 hash 值、key、value、指向下一个 Node 的引用。

当同一个桶中链表长度 ≥ 8 且数组总长度 ≥ 64 时,链表会转换为红黑树(提升查找效率从 O(n) 到 O(log n))。

当树中节点数 ≤ 6时,会退化回链表


基于泊松分布统计,链表长度达到 8 的概率极低,此时转树的收益大于维护树的开销。

2. 扩容机制

当 HashMap 中元素数量(size)超过阈值(threshold)时触发扩容。

  • 阈值 = 数组容量 × 负载因子(默认 0.75)。
  • 默认初始容量 16 → 阈值 = 12 → 存入第 13 个元素时扩容。

扩容方式(JDK 1.8 优化版)

  1. 新容量 = 旧容量 × 2(必须是 2 的幂,便于位运算)。
  2. 创建新数组,长度为原数组两倍。
  3. 迁移旧数据(重点优化):

遍历旧数组每个桶。

如果桶为空 → 跳过。

如果桶是单个节点 → 重新计算其在新数组中的位置(hash & (newCap - 1)),直接放入。

如果桶是链表 → 遍历链表,根据节点 hash 值的“新增高位”是否为 0,将节点分为“低位链表”和“高位链表”:

低位链表 → 放入新数组的原索引位置

高位链表 → 放入新数组的原索引 + 旧容量位置

红黑树 → 同样按高位拆分,若拆分后树节点数 ≤ 6,则退化为链表。就是倘若桶

JDK 1.8 优化核心

  1. 无需重新计算每个元素的 hash。
  2. 利用“旧容量”的二进制最高位来判断新位置,迁移高效。
  3. 避免了 JDK 1.7 的头插法导致的多线程下链表成环死循环问题。

3. 线程安全方面

不安全

多线程同时 put:

可能两个线程同时写入同一个桶,导致数据覆盖。

扩容时,多个线程同时迁移数据,可能导致链表结构破坏、形成环、数据丢失。

避免了死循环。就是即使是 JDK 1.8 的尾插法,也无法保证线程安全,只

4. JDK 1.7 vs 1.8 关键区别

特性JDK 1.7JDK 1.8+
数据结构数组 + 链表数组 + 链表 + 红黑树
扩容迁移头插法(易成环)尾插法 + 高低位链表(安全高效)
Hash 算法复杂扰动(9次运算)便捷扰动(1次:h ^ (h >>> 16))
链表转树不支持支持(≥8 且 cap≥64)

四、ConcurrentHashMap(JDK 1.8)

1. 底层数据结构

基本结构与 HashMap 一致:数组 + 链表/红黑树

关键区别在于线程安全机制,完全重构。

2. 线程安全原理(JDK 1.8)

采用 分段锁思想的极致优化,结合三种技术:

2.1 CAS(Compare and Swap)

  • 当某个桶为空(null)时,尝试通过 CAS 操作直接放入新节点。
  • 无锁管理,性能极高。
  • 要是 CAS 失败(说明有其他线程刚放了节点),则进入下一步。

2.2 synchronized 锁住桶的头节点

  • 若是桶非空,则对这个桶的第一个节点(头节点)加 synchronized 锁。
  • 锁粒度极小(只锁一个桶,而不是整个 Map 或 Segment)。
  • 在锁内完成链表遍历、插入、或树操作。
  • 其他线程访问其他桶时完全不受影响。

2.3 volatile + Unsafe 保证可见性

  • 数组引用 table 是 volatile 的,保证多线程间可见。
  • 运用 Unsafe 类进行底层原子操作,高效安全。

核心思想读处理完全无锁,写操作只锁单个桶,高并发下性能接近无锁。


3. 扩容机制

ConcurrentHashMap 的扩容是多线程协作式的,极大提升效率:

  1. 当一个线程发现需要扩容时,会创建一个新数组(nextTable),容量为原数组 2 倍。
  2. 该线程开始迁移数据,并在原数组对应位置放置一个ForwardingNode(转发节点),表示此桶正在迁移中。
  3. 其他线程在 put 或 get 时,如果遇到 ForwardingNode,会主动协助迁移,而不是等待。
  4. 每个线程负责迁移一段连续的桶(如 16 个),避免冲突。
  5. 所有桶迁移完成后,将 table 引用指向新数组,扩容结束。

优势:充分利用多核 CPU,并行迁移,缩短扩容停顿时间。

五、CopyOnWriteArrayList

1. 底层数据结构

核心是一个 volatile 修饰的 Object 数组,保证多线程间的可见性。

所有“写处理”(add、set、remove)必须先获取一个ReentrantLock 独占锁

所有“读操作”(get、iterator)完全无锁,直接读取当前数组。

2. 写操作机制(写时复制)

  1. 加锁(确保同一时间只有一个线程能写)。
  2. 获取当前数组的引用。
  3. 创建一个新数组,长度 = 旧数组长度 + 1(add 处理)。
  4. 将旧数组所有元素复制到新数组。
  5. 在新数组的指定位置放入新元素。
  6. 将集合的数组引用原子性地指向新数组(volatile 保证其他线程立即可见)。
  7. 释放锁。

关键点:旧数组不会被修改,读线程始终看到一个“完整一致”的快照。

3. 读操作与迭代器

get(index):直接返回当前数组对应位置的元素,无锁,极快。

iterator():返回的是创建迭代器那一刻的数组快照

即使其他线程修改了集合,迭代器遍历的仍是旧数据,不会抛 ConcurrentModificationException→ 弱一致性,可能读不到最新数据 → 适用于容忍短暂不一致的场景。

4. 适用场景与缺点

适用场景

  1. 读操作远远多于写操控(如:系统配置列表、监听器列表、黑白名单)。
  2. 读操作不能容忍阻塞(要求高吞吐、低延迟)。
  3. 信息总量不大(避免复制开销过大)。

缺点

  1. 内存占用高:写操作时同时存在新旧两个数组。
  2. GC 压力大:旧数组成为垃圾,频繁写处理导致频繁 GC。
  3. 数据延迟:读线程可能读到旧数据。
  4. 不适合实时性要求高的写运行

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

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

相关文章

干货预警!Apache SeaTunnel 助力多点 DMALL 构建数据集成平台,探索AI新零售行业应用!

🎉亲爱的社区朋友们,数据集成领域的一场知识盛宴即将来袭!9 月 30 日下午 2 点,Apache SeaTunnel 社区精心策划的又一场线上 Meetup 将准时与大家云端相见!🎉亲爱的社区朋友们,数据集成领域的一场知识盛宴即将…

Apache SeaTunnel 2.3.12 发布!核心引擎升级、连接器生态再扩张

近期,Apache SeaTunnel 2.3.12 正式发版。这是继 2.3.11 之后的又一次迭代,本周期合并 82 个 PR,提供 9 项新特性、30+ 项功能增强、20+ 处文档修正,并修复 43 个 Bug。核心改进集中在 SensorsData 与 Databend 生…

详细介绍:对于牛客网—语言学习篇—C语言入门—链表的题目解析

详细介绍:对于牛客网—语言学习篇—C语言入门—链表的题目解析pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Co…

安全认证哪家强?CISP和HCIE我选...... - 详解

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

Day17Arrays类的初步认识

package com.cc.array;import java.util.Arrays;public class ArrayDem6 {public static void main(String[] args) {int[] a = {12, 3, 43, 4, 235, 5, 6, 45, 7, 7};System.out.println(a);//[I@f6f4d33//打印数组元…

小学生模拟赛题解

A 正常做这题显然 \(10^{18}\) 是不可做的,所以问题一定出现在 gen 上。 注意到 \(7\mid2009\),换句话说,若 \(t_1=3k(k\in\mathbb N_+)\),那么 \(t_2=t_1+9\),这就导致 \(3\mid t_2\)。以此类推,会发现对于 \(\…

服务器安装docker、mysql、redis、nginx、nacos、jdk等

一、安装docker 1.1、安装必要工具 sudo yum install -y yum-utils \ device-mapper-persistent-data \ lvm21.2、进行仓库源设置 sudo yum-config-manager \ --add-repo \ https://mirrors.tuna.tsinghua.edu.cn/dock…

StringComparer.OrdinalIgnoreCase

StringComparer.OrdinalIgnoreCase 是 .NET 提供的不区分大小写、且按 Unicode 码位排序的字符串比较器,适用于哈希表、字典、集合、排序等需要显式指定比较规则的地方。1. 核心特点特性说明比较规则 不区分大小写(A…

LLM大模型:Qwen3-Next-80B中的next究竟是个啥?

1、近期,国内LLM头号玩家阿里发布了Qwen3-Next-80B模型,但从名字上看就和其之前发布的模型不同:多了next!这就奇怪了:为啥会多出一个next?这个next究竟是啥意思了?2、自从3年前 chatGPT 3.5发布后,AI又开始大火…

中了勒索病毒 peng

中了勒索病毒 peng一,中招 早上一上班,看到电脑屏幕显示这样的壁纸。 居然中招了?不敢相信。 我发现自己的网盘里的所有文件,都被加密并改名,形如 aaaa.jpg.[[VlDy9dk2RaQ1F]].[[Ruiz@firemail.cc]].peng 而且这些…

在 WSL 中通过 Bash 函数快速转换 Windows 路径为 Ansible/WSL 路径 - 教程

在 WSL 中通过 Bash 函数快速转换 Windows 路径为 Ansible/WSL 路径 - 教程pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-famil…

K8s 必备:kubectl patch 命令详解

K8s 必备:kubectl patch 命令详解精准高效的Kubernetes资源更新技巧一、为什么需要kubectl patch? 在日常Kubernetes运维工作中,我们经常需要对集群中的资源进行修改。虽然kubectl apply和kubectl edit都很常用,但…

完整教程:如何管理好上网行为,8个上网行为管控措施分享,让上网井然有序

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

深入解析:AI Ping:精准可靠的大模型服务性能评测平台

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

金融租赁公司厂商租赁业务调研报告

厂商租赁金融租赁公司厂商租赁业务调研报告 报告摘要 本报告旨在全面、深入地分析中国金融租赁公司(下称“金租公司”)厂商租赁业务的现状、模式、市场环境、监管动态、数字化转型路径及绩效评估体系。截至2025年,厂…

普科科技PKC7030H交直流电流探头应用指南​​

普科PKC7030H探头支持DC-120MHz带宽、1%精度,30A连续电流测量,适用于高频大电流交直流混合信号测试。在现代电力电子、新能源及高速数字系统的设计与调试中,对复杂电流波形的精准测量是分析效率、优化性能与保障可靠…

从“分散”到“统一”,中控技术利用SeaTunnel构建高效数据采集框架,核心数据同步任务0故障运行!

本文将深入探讨中控技术基于 Apache SeaTunnel 构建企业级数据采集框架的实践,重点分享集群高可用配置、性能调优、容错机制及数据质量监控等方面的具体思考与方案。作者 | 崔俊乐引言:对企业而言,数据采集的核心挑…

再见 Cursor,Qoder 真香!这波要改写 AI 编程格局

如果把未来 AI 编程工具的核心竞争力用一句话总结,那就是:能不能让开发者在透明化的协作中,信任它、依赖它,并且和它一起把项目养大。作者:loonggg 真心建议大家去使用一下这段时间最新推出的一款 AI 编程工具:Q…

PolarFire SoC mpfs-mmuart-interrupt 多核通信

PolarFire SoC mpfs-mmuart-interrupt 多核通信e51 :/* Clear pending software interrupt in case there was any. */clear_soft_interrupt(); set_csr(mie, MIP_MSIP);/* Raise software interrupt to wake hart…

T/B cell subtype marker - un

B cell ref: https://www.abcam.cn/primary-antibodies/b-cells-basic-immunophenotypingT cell ref: https://www.abcam.cn/primary-antibodies/t-cells-basic-immunophenotyping作者:un-define出处:https://www.cn…