集合之ArrayDeque
目录
- ArrayDeque
- 循环数组
- 属性
- 构造函数
- 详解
- 方法定义
- allocateElements(int numElements)详解
- add(E e)、addFirst(E e)、addLast(E e)、push(E e)详解
- clear()详解
- clone()详解
- contains(Object o)详解
- descendingIterator()详解
- element()、getFirst()、getLast()详解
- isEmpty()详解
- iterator()详解
- offer(E e)、offerFirst(E e)、offerLast(E e)详解
- peek()、peekFirst()、peekLast()详解
- poll()、pollFirst()、pollLast()详解
- remove()、remove(Object o)、removeFirst()、removeFirstOccurrence(Object o)、removeLast()、removeLastOccurrence(Object o)详解
- pop()详解
- size()详解
- toArray()、toArray(T[] a)详解
ArrayDeque
- 是
Deque
接口的可调整大小的数组双端队列实现,没有容量限制;根据需要增长以支持使用- 不是线程安全的;在没有外部同步的情况下,不支持多线程并发访问,禁止空元素
- 该类在用作
Stack
时比Stack
更快,当用作队列时比LinkedList
更快;大多数ArrayDeque
操作在分摊的常数时间内运行。例外情况包括remove
、removeFirstOccurrence
、removeLastOccurrence
、contains
、iterator.remove()
和批量操作,所有这些都在线性时间内运行- 该类的
iterator
方法返回的iterator
是快速失败的:如果在迭代器创建后的任何时间修改双端队列,除了通过迭代器自己的remove
方法外,迭代器通常会抛出ConcurrentModificationException
。 因此,面对并发修改,迭代器快速而干净地失败,而不是冒着在未来不确定的时间出现任意、非确定性行为的风险
循环数组
- 如上图 head 指向队头,入队加元素时,tail队尾向后移动,出队时从 head 处取出元素并移除,这样就利用了线性数组实现先进先出的队列数据结构,当 head 等于 tail 时,则表示队列为空。但是这样存在问题:当不断出队时,head 向后移动,前面空出来的空间就被浪费,导致不断入队时,需要数组扩容,出队时造成大量空间无法使用,
空间利用率低下
- 假设,如果能将前面空出来的空间也利用起来进行存储末尾的元素,那么空间使用率将提高,就需要有个循环的思维,把这种线性弯曲成一个圆环,这样就可以反复使用空出来的空间,入队时使用出队空余出来的空间解决以上的问题
- 当 head 等于 tail 时,则表示循环队列为空。head 和 tail 也是循环的,像钟表中的时针,具有周期性。这里 head 和tail 需要对长度 length 取模,这样 head 和 tail 将一直在长度范围内,可以作为数组的下标
属性
// 存储双端队列元素的数组transient Object[] elements;// 队列头部元素的索引;如果双端队列为空,则为等于 tail 的任意数字transient int head;// 将下一个元素添加到双端队列尾部的索引transient int tail;// 用于新创建的双端队列的最小容量。必须是 2 的次幂private static final int MIN_INITIAL_CAPACITY = 8;
构造函数
方法 | 作用 |
---|---|
ArrayDeque() | 构造一个空数组,初始容量足以容纳16个元素 |
ArrayDeque(Collection<? extends E> c) | 构造一个包含指定集合元素的双端队列,按照它们由集合的迭代器返回的顺序 |
ArrayDeque(int numElements) | 构造一个空数组队列,初始容量足以容纳指定数量的元素 |
详解
/*** 构造一个容量为16的空的双端队列*/public ArrayDeque() {elements = new Object[16];}/*** 构造一个容量为numElements的空的双端队列*/public ArrayDeque(int numElements) {allocateElements(numElements);}/*** 构造一个包含指定集合的元素的双端队列,按照集合迭代器返回的顺序* (集合的迭代器返回的第一个元素成为第一个元素,或双端队列的前面)*/public ArrayDeque(Collection<? extends E> c) {allocateElements(c.size());addAll(c);}
方法定义
方法 | 作用 | |
添加 | boolean add(E e) | 在此队列的末尾插入指定的元素 |
void addFirst(E e) | 在此队列前面插入指定的元素 | |
void addLast(E e) | 在此队列的末尾插入指定的元素 | |
boolean offer(E e) | 在此队列的末尾插入指定的元素 | |
boolean offerFirst(E e) | 在此队列的头部插入指定的元素 | |
boolean offerLast(E e) | 在此队列的末尾插入指定的元素 | |
void push(E e) | 将元素推送到此堆栈上 | |
删除 | void clear() | 将这个队列中所有的元素删除 |
E poll() | 检索并删除此队列的第一个元素,如果此队列为空,则返回 null | |
E pollFirst() | 检索并删除队列的第一个元素,如果此队列为空,则返回 null | |
E pollLast() | 检索并删除此队列的最后一个元素,如果此队列为空,则返回 null | |
E pop() | 从堆栈中弹出一个元素 | |
E remove() | 检索并删除由此队列的头部 | |
boolean remove(Object o) | 从此队列中删除指定元素 | |
E removeFirst() | 检索并删除此队列的第一个元素 | |
boolean removeFirstOccurrence(Object o) | 删除此队列中指定元素的第一个出现 | |
E removeLast() | 检索并删除此队列的最后一个元素 | |
boolean removeLastOccurrence(Object o) | 删除此队列中指定元素的最后一次 | |
查询 | E element() | 检索但不删除该队列的头 |
E getFirst() | 检索但不删除该队列的第一个元素 | |
E getLast() | 检索但不删除该队列的最后一个元素 | |
boolean isEmpty() | 如果此队列不包含元素,则返回 true | |
boolean contains(Object o) | 如果此队列包含指定的元素,则返回 true | |
E peek() | 检索但不删除此队列的头部,如果此队列为空,则返回 null | |
E peekFirst() | 检索但不删除此队列的第一个元素,如果此队列为空,则返回 null | |
E peekLast() | 检索但不删除此队列的最后一个元素,如果此队列为空,则返回 null | |
int size() | 返回此队列中的元素数 | |
克隆 | ArrayDeque clone() | 返回此队列的副本 |
迭代器 | Iterator iterator() | 返回此队列中元素的迭代器 |
Spliterator spliterator() | 在这个队列的元素上创建一个late-binding和快速失败的迭代器 | |
Iterator descendingIterator() | 以相反的顺序返回此队列中元素的迭代器 | |
转数组 | Object[] toArray() | 以适当的顺序返回一个包含此队列中所有元素的数组 |
T[] toArray(T[] a) | 以正确的顺序返回一个包含此队列中所有元素的数组; 返回的数组的运行时类型是指定数组的运行时类型 |
allocateElements(int numElements)详解
/*** 分配空数组以保存给定数量的元素*/private void allocateElements(int numElements) {// 创建数组,数组长度为大于当前参数的最小的2次幂整数elements = new Object[calculateSize(numElements)];}/*** 找到大于需要长度的最小的2次幂整数*/private static int calculateSize(int numElements) {// 默认为8int initialCapacity = MIN_INITIAL_CAPACITY;// 大于参数if (numElements >= initialCapacity) {initialCapacity = numElements;// 进行5次右移及位或操作initialCapacity |= (initialCapacity >>> 1);initialCapacity |= (initialCapacity >>> 2);initialCapacity |= (initialCapacity >>> 4);initialCapacity |= (initialCapacity >>> 8);initialCapacity |= (initialCapacity >>> 16);// +1initialCapacity++;// 小于0if (initialCapacity < 0)// 设置初始容量为2的30次方initialCapacity >>>= 1;}return initialCapacity;}
对于一个小于2^30的值,经过五次右移和位或操作后,可以得到一个2^k - 1的值。最后再将这个值+1,得到2^k。通过这个方法,可以将一个任意的初始值转化为2^n的值,如果本身传进来的值就是2^n的值,那么经过转化会变成2^(n+1),所以不用刻意去传入2^n的值。如果传入的值大于等于2^30,那么经过转化会变成负值,即< 0,此时会把初始值设置为2^30,即最大的容量只有2^30
add(E e)、addFirst(E e)、addLast(E e)、push(E e)详解
/*** 在此队列的末尾插入指定的元素,相当于addLast(E e)*/public boolean add(E e) {// 调用addLast方法addLast(e);return true;}/*** 在此队列前面插入指定的元素*/public void addFirst(E e) {// 如果元素为空,抛出NullPointerExceptionif (e == null)throw new NullPointerException();// 头部索引-1 与 length-1做与操作,elements[head = (head - 1) & (elements.length - 1)] = e;// 头部索引等于尾部索引时扩容if (head == tail)doubleCapacity();}/*** 在此队列的末尾插入指定的元素*/public void addLast(E e) {if (e == null)throw new NullPointerException();// 数组下标为tail的元素赋值e elements[tail] = e;// 尾部索引+1 和 length - 1进行与操作等于头部索引时扩容if ( (tail = (tail + 1) & (elements.length - 1)) == head)doubleCapacity();}/*** 将元素推送到此堆栈上,相当于addFirst(e)*/public void push(E e) {addFirst(e);}/*** 将此双端队列的容量加倍。仅在当头部和尾部缠绕变得相等时调用*/private void doubleCapacity() {// 明确头部索引等于队列尾部下一个元素索引assert head == tail;int p = head;int n = elements.length;// p索引右边的元素数int r = n - p;// 新容量为旧容量左移一位即乘以2int newCapacity = n << 1;// 如果新容量小于0,抛出IllegalStateExceptionif (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;}
clear()详解
/*** 将这个队列中所有的元素删除*/public void clear() {// 记录头部、尾部索引int h = head;int t = tail;// 不为空if (h != t) {// 重置首尾索引head = tail = 0;int i = h;int mask = elements.length - 1;do {// 清除元素elements[i] = null;i = (i + 1) & mask;} while (i != t);}}
clone()详解
/*** 返回此队列的副本*/public ArrayDeque<E> clone() {try {@SuppressWarnings("unchecked")// 克隆ArrayDeque<E> result = (ArrayDeque<E>) super.clone();// 存储双端队列元素的数组赋值result.elements = Arrays.copyOf(elements, elements.length);return result;} catch (CloneNotSupportedException e) {throw new AssertionError();}}
contains(Object o)详解
/*** 如果此队列包含指定的元素,则返回 true*/public boolean contains(Object o) {// 为空返回falseif (o == null)return false;int mask = elements.length - 1;int i = head;Object x;// 循环比较while ( (x = elements[i]) != null) {// 存在if (o.equals(x))return true;i = (i + 1) & mask;}// 不存在返回falsereturn false;}
descendingIterator()详解
/*** 以相反的顺序返回此队列中元素的迭代器*/public Iterator<E> descendingIterator() {return new DescendingIterator();}private class DescendingIterator implements Iterator<E> {private int cursor = tail;// 游标开始索引为tailprivate int fence = head;// 游标的结束索引为headprivate int lastRet = -1;public boolean hasNext() {// 不相等说明有元素return cursor != fence;}public E next() {// 相等不存在元素if (cursor == fence)throw new NoSuchElementException();// tail是下个添加元素的位置,所以要减1才是尾节点的索引cursor = (cursor - 1) & (elements.length - 1);@SuppressWarnings("unchecked")E result = (E) elements[cursor];if (head != fence || result == null)throw new ConcurrentModificationException();lastRet = cursor;return result;}public void remove() {if (lastRet < 0)throw new IllegalStateException();if (!delete(lastRet)) {// 如果从左往右移,需要将游标加1cursor = (cursor + 1) & (elements.length - 1);fence = head;}lastRet = -1;}}
element()、getFirst()、getLast()详解
/*** 检索但不删除该队列的头,相当于getFirst()*/public E element() {return getFirst();}/*** 检索但不删除该队列的第一个元素*/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;}
isEmpty()详解
/*** 如果此队列不包含元素,则返回 true*/public boolean isEmpty() {// 返回头部索引是否等于尾部索引return head == tail;}
iterator()详解
public Iterator<E> iterator() {return new DeqIterator();}private class DeqIterator implements Iterator<E> {// 后续调用 next 返回的元素索引,即游标private int cursor = head;// 记录尾部索引(也在移除时记录),以停止迭代器并检查协同修改private int fence = tail;// 最近调用 next 返回的元素索引,如果调用 remove 删除元素,则重置为 -1private int lastRet = -1;public boolean hasNext() {return cursor != fence;}public E next() {if (cursor == fence)throw new NoSuchElementException();@SuppressWarnings("unchecked")// 获取元素E result = (E) elements[cursor];if (tail != fence || result == null)throw new ConcurrentModificationException();lastRet = cursor;cursor = (cursor + 1) & (elements.length - 1);return result;}public void remove() {if (lastRet < 0)throw new IllegalStateException();if (delete(lastRet)) { // 如果左移,则在 next() 中撤消增量cursor = (cursor - 1) & (elements.length - 1);// 游标位置回退1fence = tail;}lastRet = -1;}public void forEachRemaining(Consumer<? super E> action) {Objects.requireNonNull(action);Object[] a = elements;int m = a.length - 1, f = fence, i = cursor;cursor = f;while (i != f) {@SuppressWarnings("unchecked") E e = (E)a[i];i = (i + 1) & m;if (e == null)throw new ConcurrentModificationException();action.accept(e);}}}
offer(E e)、offerFirst(E e)、offerLast(E e)详解
/*** 在此队列的末尾插入指定的元素,相当于offerLast(E e)*/public boolean offer(E e) {return offerLast(e);}/*** 在此队列的头部插入指定的元素,相当于addFirst(e)*/public boolean offerFirst(E e) {addFirst(e);return true;}/*** 在此队列的末尾插入指定的元素,相当于addLast(e)*/public boolean offerLast(E e) {addLast(e);return true;}
peek()、peekFirst()、peekLast()详解
/*** 检索但不删除此队列的头部,如果此队列为空,则返回 null,相当于peekFirst()*/public E peek() {return peekFirst();}/*** 检索但不删除此队列的第一个元素,如果此队列为空,则返回 null*/public E peekFirst() {// 获取队列头元素return (E) elements[head];}/*** 检索但不删除此队列的最后一个元素,如果此队列为空,则返回 null*/public E peekLast() {// 获取队列尾部元素return (E) elements[(tail - 1) & (elements.length - 1)];}
poll()、pollFirst()、pollLast()详解
/*** 检索并删除此队列的第一个元素,如果此队列为空,则返回 null,相当于pollFirst()*/public E poll() {return pollFirst();}/*** 检索并删除队列的第一个元素,如果此队列为空,则返回 null*/public E pollFirst() {int h = head;@SuppressWarnings("unchecked")E result = (E) elements[h];// 如果为空返回nullif (result == null)return null;// 表明head位置已为空elements[h] = null;// 重新赋值头部索引head = (h + 1) & (elements.length - 1);return result;}/*** 检索并删除此队列的最后一个元素,如果此队列为空,则返回 null*/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;}
remove()、remove(Object o)、removeFirst()、removeFirstOccurrence(Object o)、removeLast()、removeLastOccurrence(Object o)详解
/*** 检索并删除此队列的头部,相当于removeFirst()*/public E remove() {return removeFirst();}/*** 从此队列中删除指定元素,相当于removeFirstOccurrence(o)*/public boolean remove(Object o) {return removeFirstOccurrence(o);}/*** 检索并删除此队列的第一个元素,相当于pollFirst()*/public E removeFirst() {E x = pollFirst();if (x == null)throw new NoSuchElementException();return x;}/*** 删除此队列中指定元素的第一个出现*/public boolean removeFirstOccurrence(Object o) {// 为空返回falseif (o == null)return false;int mask = elements.length - 1;int i = head;Object x;// 循环元素while ( (x = elements[i]) != null) {// 删除第一个出现if (o.equals(x)) {delete(i);return true;}// 增加游标i = (i + 1) & mask;}return false;}/*** 检索并删除此队列的最后一个元素,相当于pollLast()*/public E removeLast() {E x = pollLast();if (x == null)throw new NoSuchElementException();return x;}/*** 删除此队列中指定元素的最后一次*/public boolean removeLastOccurrence(Object o) {// 为空返回falseif (o == null)return false;int mask = elements.length - 1;int i = (tail - 1) & mask;Object x;while ( (x = elements[i]) != null) {// 删除最后一次出现if (o.equals(x)) {delete(i);return true;}i = (i - 1) & mask;}return false;}
pop()详解
/*** 从堆栈中弹出一个元素,相当于removeFirst()*/public E pop() {return removeFirst();}
size()详解
/*** 返回此队列中的元素数*/public int size() {// 尾部元素下标减头部元素下标与数组长度-1做与运算return (tail - head) & (elements.length - 1);}
toArray()、toArray(T[] a)详解
/*** 以适当的顺序返回一个包含此队列中所有元素的数组,其实是调用了copyElements(T[] a)*/public Object[] toArray() {// 复制数组return copyElements(new Object[size()]);}/*** 按顺序将元素数组elements复制到指定的数组中*/private <T> T[] copyElements(T[] a) {if (head < tail) {System.arraycopy(elements, head, a, 0, size());} else if (head > tail) {int headPortionLen = elements.length - head;System.arraycopy(elements, head, a, 0, headPortionLen);System.arraycopy(elements, 0, a, headPortionLen, tail);}return a;}/*** 以正确的顺序返回一个包含此队列中所有元素的数组; * 返回的数组的运行时类型是指定数组的运行时类型*/public <T> T[] toArray(T[] a) {int size = size();// 如果指定数组小于元素数,创建新数组if (a.length < size)a = (T[])java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);// 复制数组copyElements(a);if (a.length > size)a[size] = null;return a;}
集合之ArrayDeque相关推荐
- 死磕 java集合之ArrayDeque源码分析
问题 (1)什么是双端队列? (2)ArrayDeque是怎么实现双端队列的? (3)ArrayDeque是线程安全的吗? (4)ArrayDeque是有界的吗? 简介 双端队列是一种特殊的队列,它的 ...
- java arraydeque_死磕 java集合之ArrayDeque源码分析
问题 (1)什么是双端队列? (2)ArrayDeque是怎么实现双端队列的? (3)ArrayDeque是线程安全的吗? (4)ArrayDeque是有界的吗? 简介 双端队列是一种特殊的队列,它的 ...
- 深度剖析Java集合之ArrayDeque
ArrayDeque ArrayDeque是Deque接口的一个实现,使用了可变数组,所以没有容量上的限制.同时,ArrayDeque是线程不安全的,在没有外部同步的情况下,不能再多线程环境下使用. ...
- 集合框架之ArrayDeque类详解
Java集合框架分析(Deque)---ArrayDeque类详解 目录 一.数据结构 二.类标题 三.字段 四.构造函数 五.方法分析 类型 方法 作用 添加元素 public void addFi ...
- 死磕 java集合之终结篇
概览 我们先来看一看java中所有集合的类关系图. 这里面的类太多了,请放大看,如果放大还看不清,请再放大看,如果还是看不清,请放弃. 我们下面主要分成五个部分来逐个击破. List List中的元素 ...
- Java SE第8章 Java集合
Java SE第8章 Java集合 1. 集合的概念和作用 2. 使用Lambad表达式遍历集合 3.Collection集合的常规用法 4. 使用Predicate操作集合 5.使用Iterator ...
- java queue源码_java源码解读--queue
queue接口特点:可以模拟队列行为,即"先进先出". 接口结构 queue接口继承了Collection接口,并增加了一些新方法 1 2 3 4 5 6 7 8 9 10 11 ...
- 深读源码-java集合类总结篇
概览 我们先来看一看java中所有集合的类关系图. 这里面的类太多了,请放大看,如果放大还看不清,请再放大看,如果还是看不清,请放弃. 我们下面主要分成五个部分来逐个击破. List List中的元素 ...
- 栈与队列及其应用 - 1.算术表达式求值
使用到了ArrayDeque集合:使用ArrayDeque表示Queue比LinkedList.表示Stack效率更高 ArrayDeque使用详解 package com.kiger.Demo;im ...
最新文章
- linux make makefile 内置变量 默认变量
- 【js笔记】数组那些事[0]
- Android Low Battery 低电量处理流程
- C:\Windows\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files\root\文件不断增长,如何处理?...
- 配置Django实现数据库读写分离
- Python Tricks —— 计算 1+1/2+1/4+...=2
- java 汉字 char_char 类型如何存储一个汉字的?
- presentation编程软件_编程难学?web相关知识,跟着淼哥学php全栈之路6
- 帆软超级链接使用(根据内容跳转不同页面、超级链接使用js并传参、超级链接参数传递)
- c2c网站开店的流程图_C2C电子商务网站的交易流程
- Jib快速打包Docker镜像
- 《数学建模算法与应用第二版》——chapter13.数字图像处理
- JSON Viewer有个大bug导致不得不放弃掉
- STM32单片机基于HAL库开发HC-SR04 超声波测距模块(终极版)
- ROS学习笔记(八)—— moveit!概述
- 【无标题】阳光厨房管理系统需求分析
- Java/eclipse新建项目TR_52,编写程序,完成以下功能
- 乔布斯在斯坦福大学的演讲稿【中英】
- 北京地铁21号线_北京迎来地铁大动脉,设有21站,全为地下线,沿线市民幸运了...
- android 仿qq好友列表分组效果及联系人分组效果
热门文章
- Nodejs卸载、安装及环境配置
- Scala中List的步长by
- phpstudy提示80端口被system占用
- nginx1.18.0 安装vts
- python 在 win cmd 环境中形如 '\xhh' 输出的转化
- 计算机十进制转为八位二进制,Java将十进制转换为8位二进制(Java convert from decimal to 8-bit binary)...
- 学习JBPM 工作流引擎 API方法(二)
- Muli3D 4 Calculate vertex tangent
- studio 3T连接不上mongoDB
- Cesium是什么,简介