【Java】ArrayDeque 底层的循环数组
循环数组Circular Array / Ring Buffer循环数组是ArrayDeque的核心数据结构。它的精髓在于用固定长度的数组通过下标绕回实现队列的两端操作避免数据搬移。一、普通数组做队列的问题假设用普通数组实现队列容量为 8队头在索引 0队尾在索引 5 [10, 20, 30, 40, 50, 60, _, _] 0 1 2 3 4 5 6 7出队一个元素删除 10如果要求数据连续就得把后面所有元素往前挪[20, 30, 40, 50, 60, _, _, _] ← 所有元素左移O(n)同样入队时如果在尾部也可能需要扩容和搬移。问题根源下标只能单调增长数组空间被一次性消耗。二、循环数组的核心思想关键点下标到达数组末尾后不越界而是绕回到 0。索引增长: 0 → 1 → 2 → ... → 7 → 0 → 1 → 2 ... 绕回物理上是一个线性数组逻辑上把它弯成一个环0 7 1 6 2 5 3 4这样队头、队尾都可以在这个环上自由移动删除元素时不需要搬移任何数据只需要移动 head 指针。三、两个指针head 和 tailArrayDeque内部维护两个关键字段transientObject[]elements;// 存储元素的数组transientinthead;// 队头元素的下标transientinttail;// 下一个入队位置的下标约定head指向队头元素下一个出队的位置tail指向下一个可插入的位置队尾的下一个空位数组始终有一个空位不用后面解释四、环形下标的计算这是循环数组最核心的公式。前进/后退时用按位与实现取模前提是容量为 2 的幂// 前进一格tail 后移tail(tail1)(elements.length-1);// 后退一格head 前移用于 addFirsthead(head-1)(elements.length-1);为什么能用代替%当length是 2 的幂如 8 2³length - 1 7 0b0111。(tail 1) 7: 当 tail1 8 → 1000 0111 0000 0 ✅ 绕回 当 tail1 9 → 1001 0111 0001 1 ✅ 当 tail1 3 → 0011 0111 0011 3 ✅而head - 1在 head0 时会变成 -1在补码中 -1 0xFFFFFFFF与 7 按位与得到 7(-1) 7 0xFFFFFFFF 0x7 7 ✅ 从 0 绕回到末尾这就是为什么ArrayDeque容量必须是 2 的幂。位运算比取模快得多且天然处理负数下标。五、一个完整的例子容量 8初始: head0, tail0 [_, _, _, _, _, _, _, _] addLast(A): elements[0]A, tail(01)71 [A, _, _, _, _, _, _, _] ↑head ↑tail addLast(B): elements[1]B, tail2 [A, B, _, _, _, _, _, _] ↑head ↑tail addLast(C): elements[2]C, tail3 [A, B, C, _, _, _, _, _] ↑head ↑tail pollFirst(): 返回 A, head(01)71 [_, B, C, _, _, _, _, _] ↑head ↑tail addLast(D): elements[3]D, tail4 [_, B, C, D, _, _, _, _] ↑head ↑tail现在把 tail 推到末尾绕回addLast(E): elements[4]E, tail5 addLast(F): elements[5]F, tail6 addLast(G): elements[6]G, tail7 [_, B, C, D, E, F, G, _] ↑head ↑tail addLast(H): elements[7]H, tail(71)70 ← 绕回 [_, B, C, D, E, F, G, H] ↑tail ↑head此时 tail0head1队列逻辑顺序是B C D E F G H物理上从索引 1 一直排到索引 7 再到索引 0。tail跑到了head前面。六、判断空和满为什么留一个空位有了 head 和 tail怎么区分空和满空: head tail 满: head tail ← 冲突了如果允许数组全满空和满的状态完全一样无法区分。解决方案永远留一个空槽数组最多存length - 1个元素。// 判空headtail// 判满tail 前进一格就撞上 head((tail1)(elements.length-1))head上面例子中H 入队后head1, tail0检查满(01)7 1 head→ 满此时实际存了 7 个元素B~H数组 8 格中空了索引 0tail 位置……等等这里 tail0 指向的就是那个空槽。所以容量为 8 的数组最多装 7 个元素触发扩容。七、扩容doubleCapacity当判满成立时ArrayDeque会申请一个两倍大小的新数组仍然是 2 的幂把旧数组的元素按逻辑顺序复制过去让 head 归到索引 0更新 head0, tail原元素个数privatevoiddoubleCapacity(){intnelements.length;intrn-head;// head 右边的元素个数intnewCapacityn1;// 翻倍Object[]anewObject[newCapacity];// 先复制 head 到末尾这一段System.arraycopy(elements,head,a,0,r);// 再复制 0 到 tail 这一段System.arraycopy(elements,0,a,r,head);elementsa;head0;tailn;}用上面的例子head1, tail0元素 B~H旧: [_, B, C, D, E, F, G, H] head1 新: [B, C, D, E, F, G, H, _, _, _, _, _, _, _, _, _] ↑head0 ↑tail7这样逻辑顺序就拉直了后续操作可以继续。八、性能与特点操作复杂度说明addFirst/addLast均摊 O(1)扩容时单次 O(n)均摊后 O(1)pollFirst/pollLastO(1)只移动指针不搬移数据peekFirst/peekLastO(1)直接读下标随机访问get(i)O(1)通过(head i) mask定位对比 LinkedList空间循环数组连续内存缓存友好LinkedList 每个节点额外两个指针内存碎片多。速度ArrayDeque通常比LinkedList快尤其在大数据量下。限制ArrayDeque不允许null元素因为要用 null 表示空槽。与%取模的对比ArrayDeque用 (length-1)前提是长度 2 的幂速度极快。如果容量不是 2 的幂比如某些自定义环形缓冲区就得用(i 1) % n或者用条件判断i (i 1 n) ? 0 : i 1。九、一句话总结循环数组 一个线性数组 两个会绕圈的下标。它把数组首尾相连成环删除和插入只需移动指针无需搬移数据用 2 的幂容量 位与运算实现 O(1) 的环形寻址用留一个空槽区分空与满满了就翻倍扩容并拉直。这正是ArrayDeque能同时高效支持栈push/pop和队列offer/poll两种用法的底层原因。

相关新闻

Python生态强大,学习友好,建议收藏!

Python生态强大,学习友好,建议收藏!

最近在学习主流编程语言时,发现Python的生态真是强大到离谱!从简单的脚本到复杂的后端开发,Python几乎无所不能。它的语法简洁清晰,学习曲线友好,特别适合新手入门。更让我惊喜的是,Python的生态系统非常完…

2026/10/9 8:42:19 阅读更多 →
PhysStream 源码级拆解:流式视频生成如何做到“边生成边控制“

PhysStream 源码级拆解:流式视频生成如何做到“边生成边控制“

从今天觉醒,技术赋予每一个人数字生命PhysStream 源码级拆解:流式视频生成如何做到"边生成边控制" 如果你最近在追视频生成,会发现一个尴尬的现实:模型能生成惊艳的画面,但你几乎无法在生成过程中"伸手进去"改…

2026/10/9 8:42:08 阅读更多 →
形体管理机构那家好?被动矫正适合懒人塑身吗

形体管理机构那家好?被动矫正适合懒人塑身吗

形体管理机构哪家好?被动矫正适合懒人塑身吗在探讨形体管理机构哪家好这一话题时,核心考量点不应仅局限于品牌知名度,更在于其技术模式是否具备差异化优势,以及能否提供可视化的即时体验。目前市场主流的解决方案中,以…

2026/10/8 6:47:44 阅读更多 →

最新新闻

TX1串口调试实战:ttyTHS1与ttyTHS2区别、配置与避坑指南

TX1串口调试实战:ttyTHS1与ttyTHS2区别、配置与避坑指南

1. 从一块开发板的调试口说起:tx1串口到底特殊在哪很多人第一次接触tx1的串口,都是因为系统起不来、网络不通、或者想进控制台看看内核打印。这块板子的串口不像普通单片机那样随便找个UART就能用,它的调试串口和通用串口在设备树里是分开配置…

2026/10/9 12:44:11 阅读更多 →
EmbeddingGemma浏览器原生推理:纯前端文本向量化实战

EmbeddingGemma浏览器原生推理:纯前端文本向量化实战

1. 项目概述:为什么“EmbeddingGemma 浏览器直接试用”这件事值得认真对待最近在多个技术社区和开发者群聊里,频繁刷到“EmbeddingGemma 浏览器直接试用”这个短语——它不像传统模型部署那样强调服务器、GPU或Docker,而是把“浏览器”和“直…

2026/10/9 12:44:11 阅读更多 →
Vue项目跨域问题全解析:Proxy、CORS、JSONP及Nginx配置实践

Vue项目跨域问题全解析:Proxy、CORS、JSONP及Nginx配置实践

做 Vue 项目做到第 49 天,正好卡在前后端联调这个坎上。你本地跑着npm run dev,接口地址填的是后端同事的http://192.168.x.x:8080,一刷新页面浏览器控制台直接给你一行红字:Access to XMLHttpRequest at ... from origin http://…

2026/10/9 12:44:11 阅读更多 →
纯前端同学录模板:零后端、可编辑、静态部署的班级网站

纯前端同学录模板:零后端、可编辑、静态部署的班级网站

简介:这是一份面向网页开发初学者与班级信息化建设需求者的同学录网页实战模板,解决校园场景下轻量级班级信息管理、通讯录维护与动态公告发布的实际问题。资源为86KB的RAR压缩包,共27个文件,包含23个GIF图片(用于导航…

2026/10/9 12:44:11 阅读更多 →
浏览器里跑《我的世界》:MC.JS 的 WebGL 与 WebAssembly 工程实践

浏览器里跑《我的世界》:MC.JS 的 WebGL 与 WebAssembly 工程实践

1. 项目概述:为什么要在浏览器里跑《我的世界》?“浏览器里跑《我的世界》”——这句话刚听上去像一句玩笑,但当你双击一个.html文件,网页加载几秒后,熟悉的方块世界在 Chrome 或 Edge 里稳稳渲染出来,角色…

2026/10/9 12:44:11 阅读更多 →
Linux内核参数调优实战:sysctl、内存、TCP与文件系统配置指南

Linux内核参数调优实战:sysctl、内存、TCP与文件系统配置指南

简介:一份面向Linux系统管理员与运维工程师的《Linux操作系统调优参数》文档,重点解决网络密集型场景下的TCP/IP性能调优问题。内容围绕/proc/sys/net目录中的核心参数展开:rmem_max与wmem_max用于调整TCP收发缓冲区上限,避免高负…

2026/10/9 12:43:10 阅读更多 →

日新闻

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API这个话题,隔三差五就会在群里被翻出来讨论一次。上周还有个同事线上处理一个订单超时问题,排查到最后发现是ZonedDateTime序列化后时区丢了,用户在下单当天晚上看到的时间整整差了8个小时。这类问题几乎每个做Java开发的人都遇到过…

2026/10/9 0:00:49 阅读更多 →
EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

前几个月我手头有好几台机器需要互相访问:办公室台式机、家里 NAS、还有一台云主机。如果只是偶尔传个文件倒还好,问题是工作场景经常要在几处环境之间来回切换,每次都先登录跳板机再层层代理,实在折腾。我先后试过端口映射、自建…

2026/10/9 0:00:49 阅读更多 →
AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent 这个词在过去一年里被反复提及,但真正动手搭过一套能跑起来的 Agent 系统的人都知道,从"知道它是什么"到"让它稳定干活"之间隔着一整套工程决策。我前后参与过几个 Agent 项目的落地,从最初用现成框架拼装&…

2026/10/9 0:01:50 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:32 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:40 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 10:11:06 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 21:13:17 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 6:17:20 阅读更多 →