Java ArrayDeque源码剖析

LinkedList实现了队列接口Queue和双端队列接口Deque,Java容器类中还有一个双端队列的实现类ArrayDeque,它是基于数组实现的。我们知道,一般而言,由于需要移动元素,数组的插入和删除效率比较低,但ArrayDeque的效率却非常高,它是怎么实现的呢?本文就来详细探讨。

实现原理

ArrayDeque内部主要有如下实例变量:

private transient E[] elements;
private transient int head;
private transient int tail;

elements就是存储元素的数组。ArrayDeque的高效来源于head和tail这两个变量,它们使得物理上简单的从头到尾的数组变为了一个逻辑上循环的数组,避免了在头尾操作时的移动。

对于一般数组,比如arr,第一个元素为arr[0],最后一个为arr[ar.length-1]。但对于ArrayDeque中的数组,它是一个逻辑上的循环数组,所谓循环是指元素到数组尾之后可以接着从数组头开始,数组的长度、第一个和最后一个元素都与head和tail这两个变量有关,具体来说:
1)如果head和tail相同,则数组为空,长度为0。

2)如果tail大于head,则第一个元素为elements[head],最后一个为elements[tail-1],长度为tail-head,
元素索引从head到tail-1。
3)如果tail小于head,且为0,则第一个元素为elements[head],最后一个为elements[elements.length-
1],元素索引从head到elements.length-1。
4)如果tail小于head,且大于0,则会形成循环,第一个元素为elements[head],最后一个是
elements[tail-1],元素索引从head到elements.length-1,然后再从0到tail-1。

ArrayDeque默认构造方法的代码为:

public ArrayDeque() {elements = (E[]) new Object[16];
}

代码不是简单地分配给定的长度,而是调用了allocateElements。这个方法的代码看上去比较复杂,它主要就是在计算应该分配的数组的长度,计算逻辑如下:
1)如果numElements小于8,就是8。
2)在numElements大于等于8的情况下,分配的实际长度是严格大于numElements并且为2的整数次幂的最小数。比如,如果numElements为10,则实际分配16,如果num-Elements为32,则为64。
为什么要严格大于numElements呢?因为循环数组必须时刻至少留一个空位,tail变量指向下一个空位,为了容纳numElements个元素,至少需要numElements+1个位置。

add方法的代码为:

public boolean add(E e) {addLast(e);return true;}

addLast的代码为:

public void addLast(E e) {if(e == null)throw new NullPointerException();elements[tail] = e;if( (tail = (tail + 1) & (elements.length - 1)) == head)doubleCapacity();
}

将元素添加到tail处,然后tail指向下一个位置,如果队列满了,则调用doubleCapa-city扩展数组。tail的下一个位置是(tail+1)&(elements.length-1),如果与head相同,则队列就满了。
进行与操作保证了索引在正确范围,与(elements.length-1)相与就可以得到下一个正确位置,是因为elements.length是2的幂次方,(elements.length-1)的后几位全是1,无论是正数还是负数,与(elements.length-1)相与都能得到期望的下一个正确位置。

addFirst方法的代码为:

public void addFirst(E e) {if(e == null)throw new NullPointerException();elements[head = (head - 1) & (elements.length - 1)] = e;if(head == tail)doubleCapacity();
}

在头部添加,要先让head指向前一个位置,然后再赋值给head所在位置。head的前一个位置是(head-1)&(elements.length-1)。刚开始head为0,如果elements.length为8,则(head-1)&(elements.length-1)的结果为7。

removeFirst方法的代码为:

public E removeFirst() {E x = pollFirst();if(x == null)throw new NoSuchElementException();return x;
}

主要调用了pollFirst方法,pollFirst方法的代码为:

public E pollFirst() {int h = head;E result = elements[h]; //Element is null if deque emptyif(result == null)return null;elements[h] = null;     //Must null out slothead = (h + 1) & (elements.length - 1);return result;
}

ArrayDeque实现了双端队列,内部使用循环数组实现,这决定了它有如下特点。
1)在两端添加、删除元素的效率很高,动态扩展需要的内存分配以及数组复制开销可以被平摊,具体来说,添加N个元素的效率为O(N)。
2)根据元素内容查找和删除的效率比较低,为O(N)。
3)与ArrayList和LinkedList不同,没有索引位置的概念,不能根据索引位置进行操作。ArrayDeque和LinkedList都实现了Deque接口,应该用哪一个呢?如果只需要Deque接口,从两端进行操作,一般而言,ArrayDeque效率更高一些,应该被优先使用;如果同时需要根据索引位置进行操作,或者经常需要在中间进行插入和删除,则应该选LinkedList。
 

以上就是ArrayDeque的基本原理,内部它是一个动态扩展的循环数组,通过head和tail变量维护数组的开始和结尾,数组长度为2的幂次方,使用高效的位操作进行各种判断,以及对head和tail进行维护。

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

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

相关文章

深度学习基础之《TensorFlow框架(5)—会话》

一、会话 2.x版本由于是即时执行模式,所以不需要会话。但是可以手工开启会话 1、什么是会话 一个运行TensorFlow operation的类。会话包含以下两种开启方式 (1)tf.compat.v1.Session:用于完整的程序当中 (2&#xff…

微服务部署:金丝雀发布、蓝绿发布和滚动发布的对比

金丝雀发布、蓝绿发布和滚动发布的对比 金丝雀发布、蓝绿发布和滚动发布都是软件发布策略,它们都旨在降低发布风险并提高发布速度。但是,这三种策略在工作方式、优缺点等方面存在一些差异。 工作方式 金丝雀发布:将新版本软件逐步发布给用…

【nginx实践连载-4】彻底卸载Nginx(Ubuntu)

步骤1:停止Nginx服务 打开终端(Terminal)。停止Nginx服务:sudo systemctl stop nginx步骤2:卸载Nginx软件包 运行以下命令卸载Nginx软件包:sudo apt purge nginx nginx-common nginx-core步骤3&#xff1…

速盾网络:CDN用几天关了可以吗?安全吗?

在使用CDN(内容分发网络)时,有时候会考虑暂时关闭或暂停使用CDN服务的情况。但是,对于关闭CDN服务的时间长短以及安全性问题,很多人可能存在疑问。在本文中,我们将讨论CDN使用中关闭几天是否安全以及相关注…

一、直方图相关学习

1、灰度直方图 1.1 基本概念和作用 表示图像中每个灰度级别的像素数量。用于分析图像的亮度分布情况。 1.2 代码示例 参数介绍 hist cv2.calcHist(images, channels, mask, histSize, ranges, hist, accumulate)-images:输入图像的列表。对于灰度图像&#xff0…

Prometheus安装

一、Prometheus的简介 Prometheus是一种开源的监控和警报工具,用于收集、存储和查询各种系统和服务的指标数据。它具有灵活的查询语言和强大的可视化功能,可用于实时监控应用程序性能和状态。 二、Prometheus下载 1、官网下载地址 下载Prometheus 2、P…

【regex】正则表达式

集合 [0-9.] [0-9.\-] 例子 正则表达式,按照规则写,写的时候应该不算困难,但是可读性差 不同语言中regex会有微小的差异 vim 需要转义, perl/python中不需要转义 锚位 \b am\b i am 命名 / 命名捕获组 ( 捕获组(…

167基于matlab的根据《液体动静压轴承》编写的有回油槽径向静压轴承的程序

基于matlab的根据《液体动静压轴承》编写的有回油槽径向静压轴承的程序,可显示承载能力、压强、刚度及温升等图谱.程序已调通,可直接运行。 167 显示承载能力、压强、刚度及温升 (xiaohongshu.com)https://www.xiaohongshu.com/explore/65d212b200000000…

numpy模块:从基础到高级的完整指南【第88篇—NumPy数组操作】

numpy模块:从基础到高级的完整指南 在Python的科学计算领域,NumPy模块是一个不可或缺的利器。它提供了丰富的数学函数和矩阵操作,使得数据处理、分析和科学计算变得更加高效。本文将带你初步了解NumPy模块,并通过实例代码深入解析…

2.17日学习打卡----初学Dubbo(二)

2.17日学习打卡 目录: 2.17日学习打卡一. Dubbo入门案例需求介绍配置开发环境dubbo-producerdubbo-consumer运行测试IDEA开启DashBoard面板 二. Dubbo高级特性序列化协议安全地址缓存超时时间与配置覆盖关系重试机制多版本负载均衡集群容错服务降级服务限流原理服务限流实现结果…

SpringBoot整理-Actuator

Spring Boot Actuator是Spring Boot的一个重要特性,它提供了一系列生产就绪的特性,帮助你监控和管理你的应用。Actuator主要用于暴露应用的内部状态和指标,这些信息对于维护和监控应用非常有用。 关键特性 监控和管理应用:提供了各种端点(Endpoints)来暴露应用信息,如健康…

【Spring Boot 3】【JPA】@ElementCollection处理一对多关系

【Spring Boot 3】【JPA】@ElementCollection处理一对多关系 背景介绍开发环境开发步骤及源码工程目录结构背景 软件开发是一门实践性科学,对大多数人来说,学习一种新技术不是一开始就去深究其原理,而是先从做出一个可工作的DEMO入手。但在我个人学习和工作经历中,每次学习…

[深度学习] 深入理解什么是卷积神经网络

​ 🌈 博客个人主页:Chris在Coding 🎥 本文所属专栏:[深度学习] ❤️ 热门学习专栏:[Linux学习] ⏰ 我们仍在旅途 目录 1.卷积的定义 2.卷积的"卷"在哪里 3.什么又是卷积神…

Kerberos认证详解

对于Kerberos认证,可以大概如下图: 但是如果是详细的话,Kerberos认证过程可以分为三个部分,六个过程 1.AS-REQ&&AS-REP 1.AS-REQ 当域内某个用户输入了账号或者密码的时候,客户端就会发送一个authenticator…

企业大宽带服务器用哪里最合适

如今,数字经济的发展速度不断加快,进入数字化跑道的企业,每天都在大量输出、共享、存储数字内容,想要更高效、安全地让用户看到内容,企业的服务器需要满足大带宽、低延时、高并发等要求。 中小企业受限于资金、资源等…

【教程】Linux使用aria2c多线程满速下载

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhang.cn] 安装aria2c&#xff1a; sudo apt-get install aria2多线程下载&#xff1a; aria2c -x 16 -s 16 <url> 比如&#xff1a; aria2c -x 16 -s 16 http://images.cocodataset.org/zips/test2017.zip

白话微机:8.解释FPGA以及一些考研面试问题

一. 前言&#xff08;更新世界观&#xff09; 在“微机世界”&#xff0c;普通的城市(单片机)里&#xff0c;人又有一个别的名字叫做“数据”&#xff0c;人有0有1&#xff1b;人们也有住房&#xff0c;这些住房在这个世界叫做“存储器”&#xff1b;地上有路&#xff0c;这些路…

人工智能驱动下的网络安全六大发展趋势

2023年生成式AI技术的异军突起&#xff0c;给动荡的全球网络安全威胁态势增加了不确定性、不对称性和复杂性。在2024年&#xff0c;随着生成式AI攻防对抗、网络犯罪规模化、全球大选与地缘政治动荡和新型网络威胁的快速增长&#xff0c;网络安全也将迎来一次重大变革和洗牌。 以…

centos 安装 maven

在 CentOS 系统上安装 Apache Maven 的详细步骤如下&#xff1a; 下载 Maven&#xff1a; 打开终端&#xff0c;使用 wget 命令从 Apache 官方网站下载 Maven。请替换 <version> 为实际的 Maven 版本号&#xff08;例如&#xff1a;3.8.6&#xff09;。 wget https://arc…

mmap映射文件使用示例

mmap 零拷贝技术可以应用于很多场景&#xff0c;其中一个典型的应用场景是网络文件传输。 假设我们需要将一个大文件传输到远程服务器上。在传统的方式下&#xff0c;我们可能需要将文件内容读入内存&#xff0c;然后再将数据从内存复制到网络协议栈中&#xff0c;最终发送到远…