java源码-ArrayDeque
开篇
Deque 接口继承自 Queue接口,但 Deque 支持同时从两端添加或移除元素,因此又被成为双端队列。鉴于此,Deque 接口的实现可以被当作 FIFO队列使用,也可以当作LIFO队列(栈)来使用。官方也是推荐使用 Deque 的实现来替代 Stack。
ArrayDeque 是 Deque 接口的一种具体实现,是依赖于可变数组来实现的。ArrayDeque 没有容量限制,可根据需求自动进行扩容。ArrayDeque不支持值为 null 的元素。
ArrayDeque类图
ArrayDeque的类变量和构造函数
ArrayDeque的类变量当中数组elements用来保存队列元素,head指针指向第一个存储元素,tail指向最后一个元素的下一个位置。
ArrayDeque的calculateSize的逻辑非常巧妙,用于计算大于numElements的最小的2*n次方的值。
public class ArrayDeque<E> extends AbstractCollection<E> implements Deque<E>, Cloneable, Serializable { // ArrayDeque采用数组来保存元素,通过头尾指针实现循环数组 transient Object[] elements; // non-private to simplify nested class access // 第一个元素和最后一个元素的位置 transient int head; transient int tail; private static final int MIN_INITIAL_CAPACITY = 8; // 计算数组大小的方式,实现大于numElements的最小的2*n次方的数字 private static int calculateSize(int numElements) { int initialCapacity = MIN_INITIAL_CAPACITY; if (numElements >= initialCapacity) { initialCapacity = numElements; initialCapacity |= (initialCapacity >>> 1); initialCapacity |= (initialCapacity >>> 2); initialCapacity |= (initialCapacity >>> 4); initialCapacity |= (initialCapacity >>> 8); initialCapacity |= (initialCapacity >>> 16); initialCapacity++; if (initialCapacity < 0) // Too many elements, must back off initialCapacity >>>= 1;// Good luck allocating 2 ^ 30 elements } return initialCapacity; } // 分配数组的大小,calculateSize计算大小 private void allocateElements(int numElements) { elements = new Object[calculateSize(numElements)]; }
ArrayDeque的add相关操作
ArrayDeque的add操作支持head端插入和tail端插入,head端插入是先计算位置后插入元素,tail端的插入是先保存元素后计算位置,所以会造成head的指针指向第一个元素的位置,tail指向最后一个元素的下一个位置。
ArrayDeque的扩容时机是在head和tail相等的时候,根据上面分析我们可以得出ArrayDeque的容量达到(旧容量-1)的时候进行扩容。
ArrayDeque的头部插入过程是回退head指针后添加元素。
ArrayDeque的尾部插入过程是添加元素后前进tail指针。
public boolean offerFirst(E e) { addFirst(e); return true; } public void addFirst(E e) { if (e == null) throw new NullPointerException(); //head往后移动一个位置放置新插入的元素 //head指向第一个元素的下标 elements[head = (head - 1) & (elements.length - 1)] = e; if (head == tail) doubleCapacity(); } public boolean offerLast(E e) { addLast(e); return true; } //tail往前移动一个位置放置新插入的元素 //tail指向末尾元素的下一个位置,注意是末尾元素的下一个位置 public void addLast(E e) { if (e == null) throw new NullPointerException(); elements[tail] = e; //tail循环以后和head保持一个空余位置,也就是说head=tail+1的时候进行扩容 if ( (tail = (tail + 1) & (elements.length - 1)) == head) doubleCapacity(); }
ArrayDeque的remove相关操作
ArrayDeque的remove操作包括从head开始删除和从tail开始删除。
- 头部删除pollFirst()方法返回head指针指向的元素同时向后移动一个位置
- 尾部删除pollLast()方法返回tail指针指向位置的前一个位置的元素后tail指针往前移动一个位置
public E removeFirst() { E x = pollFirst(); if (x == null) throw new NoSuchElementException(); return x; } public E removeLast() { E x = pollLast(); if (x == null) throw new NoSuchElementException(); return x; } public E pollFirst() { int h = head; @SuppressWarnings("unchecked") E result = (E) elements[h]; // Element is null if deque empty if (result == null) return null; elements[h] = null; // Must null out slot head = (h + 1) & (elements.length - 1); return result; } public E pollLast() { int t = (tail - 1) & (elements.length - 1); @SuppressWarnings("unchecked") E result = (E) elements[t]; if (result == null) return null; elements[t] = null; tail = t; return result; } public E getFirst() { @SuppressWarnings("unchecked") E result = (E) elements[head]; if (result == null) throw new NoSuchElementException(); return result; } public E getLast() { @SuppressWarnings("unchecked") E result = (E) elements[(tail - 1) & (elements.length - 1)]; if (result == null) throw new NoSuchElementException(); return result; } @SuppressWarnings("unchecked") public E peekFirst() { // elements[head] is null if deque empty return (E) elements[head]; } @SuppressWarnings("unchecked") public E peekLast() { return (E) elements[(tail - 1) & (elements.length - 1)]; }
ArrayDeque的扩容过程
ArrayDeque的扩容过程如下:
- 以2倍速率进行扩容(int newCapacity = n << 1)
- 拷贝下标head到数组末尾的元素到新数组
- 拷贝下标0到tail指针的元素到新数组
private void doubleCapacity() { assert head == tail; int p = head; int n = elements.length; int r = n - p; // number of elements to the right of p int newCapacity = n << 1; if (newCapacity < 0) throw new IllegalStateException("Sorry, deque too big"); Object[] a = new Object[newCapacity]; System.arraycopy(elements, p, a, 0, r); System.arraycopy(elements, 0, a, r, p); elements = a; head = 0; tail = n; }
ArrayDeque的操作图解过程
参考文章

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
一文看懂混淆代码——Java Decompiled过程和代码阅读
本文不是讲解传统源码的阅读, 而是『别人的APK源码』该怎么阅读。 项目背景 此前,由于工作的原因,反编译竞品SDK,通过逆向的手段修复其源码并编译运行。 前端之下,本无秘密。相对于前端源码,Android APK源码并不是那么容易获取。由于Android这样一个开发的系统和使用java编写应用使得源码的获取成为现实。本文不过多介绍如果脱壳dex,或者动态调试apk,着重聊一下如何阅读jadx APK/DEX/JAR之后的『源码』。 反编译概念 什么是反编译?Executable->Human Readable:机器可执行转变为程序员可读。后面通过APK打包过程分析打包产物和从打包产物到可读产物。 注:apk反编译生成的一般为Smali或java decompiled(后简称jd)代码。 Android源码的打包过程 原官网较为完整的打包过程: image.png APK打包流程 打包资源文件,生成R.java文件 通过aapt打包res资源文件,生成R.java、resources.arsc和res文件(二进制 & 非二进制如res/raw和pic保持原样) 处理aid...
- 下一篇
初识 Spring(04)---(bean属性)
bean属性 1.id、name都可以表示bean的名字 id:以前属性中不能有特殊字符,现在放特殊字符也没关系 name:属性可以有特殊字符 文件目录: 代码:配置文件:applicationContext.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="car" class="com.neudeu.po.Car"></bean> </beans> Test.java package com.ne...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS7,CentOS8安装Elasticsearch6.8.6
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- CentOS8编译安装MySQL8.0.19
- CentOS关闭SELinux安全模块
- Red5直播服务器,属于Java语言的直播服务器
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池