ArrayDeque

ArrayDeque 能解决什么问题?什么时候使用 ArrayDeque?

1)Deque 接口大小可变的循环数组实现,ArrayDeque 没有容量限制并会按需增长。
2)ArrayDeque 的容量为 2 的幂,因为索引的计算是通过 & 操作实现的。
3)ArrayDeque 不是线程安全的,ArrayDeque 不允许使用 null 元素。
4)ArrayDeque 的大多数操作基于平摊常数时间,除了 remove* 和 contains。
5)ArrayDeque 返回的 iterator 是快速失败的。

如何使用 ArrayDeque?

1)ArrayDeque 用作栈时,性能优于 Stack,用作队列时,性能优于 LinkedList。

使用 ArrayDeque 有什么风险?

1)ArrayDeque 的容量为 2 的幂,存在一定的内存浪费。

ArrayDeque 核心操作的实现原理?

  • 创建实例
    /**
* ArrayDeque 底层存储元素的对象数组,没有持有元素的 slot 总是 null,
* 对象数组总是至少有一个可用的 slot(尾部 slot 总是 null)
*/
transient Object[] elements; /**
* 头部元素的索引
*/
transient int head; /**
* 尾部元素的索引
*/
transient int tail; /**
* 创建一个可用容量为 15 的空 ArrayDeque 实例
*/
public ArrayDeque() {
elements = new Object[16];
} /**
* 创建一个可用容量为 numElements 的空 ArrayDeque 实例
*/
public ArrayDeque(int numElements) {
elements =
/**
* 1)numElements < 1 时,值为 1
* 2)numElements == Integer.MAX_VALUE 时,值为 Integer.MAX_VALUE
* 3)否则值为 numElements+1
*/
new Object[numElements < 1 ? 1 :
numElements == Integer.MAX_VALUE ? Integer.MAX_VALUE :
numElements + 1];
}

单向队列相关操作

  • 将元素添加到队列尾部:offer、add
    /**
* 将元素插入到单向队列尾部
*/
@Override
public boolean offer(E e) {
return offerLast(e);
} /**
* 将元素插入到双向队列尾部
*/
@Override
public boolean offerLast(E e) {
addLast(e);
return true;
} /**
* 将目标元素添加到单向队列尾部
*/
@Override
public void addLast(E e) {
if (e == null) {
throw new NullPointerException();
}
// 读取底层对象数组
final Object[] es = elements;
// 由于 tail 总是为 null,可以直接新增元素
es[tail] = e;
// 累加尾部索引值,如果尾部和头部索引重叠,则需要执行扩容
if (head == (tail = ArrayDeque.inc(tail, es.length))) {
// 底层数组扩容
grow(1);
}
} /**
* 基于 modulus 循环递增目标索引 i
*/
static final int inc(int i, int modulus) {
// 如果索引递增后,超出数组的有效索引值,则循环移动到数组头部
if (++i >= modulus) {
i = 0;
}
return i;
} /**
* 递增 ArrayDeque 可用容量,以至少能容纳 needed 个新元素
*/
private void grow(int needed) {
// 读取旧长度
final int oldCapacity = elements.length;
int newCapacity;
/**
* 计算需要扩大的容量值
* 1)旧容量 < 64,则执行【双倍+2】扩容
* 2)否则执行 1.5 倍向下取整扩容
*/
final int jump = oldCapacity < 64 ? oldCapacity + 2 : oldCapacity >> 1;
/**
* 1)扩容值 < 所需可用容量
* 2)新容量 > Integer.MAX_VALUE - 8
*/
if (jump < needed
|| (newCapacity = oldCapacity + jump) - MAX_ARRAY_SIZE > 0) {
// 进行二次扩容
newCapacity = newCapacity(needed, jump);
}
// 拷贝原数组并写入
final Object[] es = elements = Arrays.copyOf(elements, newCapacity);
// Exceptionally, here tail == head needs to be disambiguated
/**
* 1)尾部索引在前
* 2)头部和尾部重合
* 并且头部元素不为 null
*/
if (tail < head || tail == head && es[head] != null) {
// 计算新增加的可用容量值
final int newSpace = newCapacity - oldCapacity;
// head 处以及之后的所有元素整体右移 oldCapacity - head 个位置,使得数组元素连续
System.arraycopy(es, head,
es, head + newSpace,
oldCapacity - head);
// 将原来填充元素的 slot 置为 null
for (int i = head, to = head += newSpace; i < to; i++) {
es[i] = null;
}
}
} /** Capacity calculation for edge conditions, especially overflow. */
private int newCapacity(int needed, int jump) {
final int oldCapacity = elements.length, minCapacity;
// 1)【旧容量+所需新容量】超出,Integer.MAX_VALUE - 8
if ((minCapacity = oldCapacity + needed) - MAX_ARRAY_SIZE > 0) {
if (minCapacity < 0) {
throw new IllegalStateException("Sorry, deque too big");
}
// 返回 Integer.MAX_VALUE
return Integer.MAX_VALUE;
}
// 2)批量添加集合中元素时触发,所需容量 > 新扩容容量,返回【旧容量+所需容量】
if (needed > jump) {
return minCapacity;
}
/**
* 走到这里满足三个条件
* 1)need <= jump
* 2)oldCapacity + needed <= MAX_ARRAY_SIZE
* 3)oldCapacity + jump >= MAX_ARRAY_SIZE
*/
return oldCapacity + jump - MAX_ARRAY_SIZE < 0
? oldCapacity + jump
: MAX_ARRAY_SIZE;
} /**
* 将目标元素添加到单向队列尾部,添加成功返回 true
*/
@Override
public boolean add(E e) {
addLast(e);
return true;
}
  • 查看队列头部元素:peek、element【单向队列为空时抛出 NoSuchElementException 异常】
    /**
* 查看单向队列的头部元素
*/
@Override
public E peek() {
return peekFirst();
} @Override
public E peekFirst() {
return ArrayDeque.elementAt(elements, head);
} /**
* 返回底层对象数组中指定索引处的元素
*/
@SuppressWarnings("unchecked")
static final <E> E elementAt(Object[] es, int i) {
return (E) es[i];
} /**
* 单向队列为空时抛出 NoSuchElementException 异常
*/
@Override
public E element() {
return getFirst();
}
  • 移除并返回头部元素:poll、remove【单向队列为空时抛出 NoSuchElementException 异常】
    /**
* 移除并返回单向队列的头部元素,如果队列为空,则返回 null
*/
@Override
public E poll() {
return pollFirst();
} @Override
public E pollFirst() {
final Object[] es;
final int h;
// 读取头部元素
final E e = ArrayDeque.elementAt(es = elements, h = head);
if (e != null) {
// 如果元素存在,则将头部置为 null
es[h] = null;
// 循环递增头部索引
head = ArrayDeque.inc(h, es.length);
}
return e;
} /**
* 单向队列为空时抛出 NoSuchElementException 异常
*/
@Override
public E remove() {
return removeFirst();
}

双向队列相关操作

  • 将元素添加到队列头部:offerFirst、addFirst
    /**
* 将元素插入到双端队列的头部,插入成功返回 true
*/
@Override
public boolean offerFirst(E e) {
addFirst(e);
return true;
} /**
* 将元素插入到双端队列的头部
*/
@Override
public void addFirst(E e) {
if (e == null) {
throw new NullPointerException();
}
final Object[] es = elements;
es[head = ArrayDeque.dec(head, es.length)] = e;
if (head == tail) {
grow(1);
}
} /**
* 基于 modulus 循环递减目标索引
*/
static final int dec(int i, int modulus) {
if (--i < 0) {
// 如果小于 0,则移动到对象数组尾部
i = modulus - 1;
}
return i;
}
  • 将元素添加到队列尾部:offerLast、addLast
    /**
* 将元素插入到双端队列尾部,插入成功返回 true
*/
@Override
public boolean offerLast(E e) {
addLast(e);
return true;
} /**
* 将目标元素添加到双端队列尾部
*/
@Override
public void addLast(E e) {
if (e == null) {
throw new NullPointerException();
}
// 读取底层对象数组
final Object[] es = elements;
// 由于 tail 总是为 null,可以直接新增元素
es[tail] = e;
// 累加尾部索引值,如果尾部和头部索引重叠,则需要执行扩容
if (head == (tail = ArrayDeque.inc(tail, es.length))) {
// 底层数组扩容
grow(1);
}
}
  • 查看队列头部元素:peekFirst、getFirst【双向队列为空时抛出 NoSuchElementException 异常】
    /**
* 查看双端队列的头部元素
*/
@Override
public E peekFirst() {
return ArrayDeque.elementAt(elements, head);
} /**
* @throws NoSuchElementException {@inheritDoc}
*/
@Override
public E getFirst() {
final E e = ArrayDeque.elementAt(elements, head);
if (e == null) {
throw new NoSuchElementException();
}
return e;
}
  • 查看队列尾部元素:peekLast、getLast【双向队列为空时抛出 NoSuchElementException 异常】
    /**
* 查看双端队列的尾部元素
*/
@Override
public E peekLast() {
final Object[] es;
// 由于 tail 总是 null,实际的尾部元素在 tail-1 索引处
return ArrayDeque.elementAt(es = elements, ArrayDeque.dec(tail, es.length));
} /**
* @throws NoSuchElementException {@inheritDoc}
*/
@Override
public E getLast() {
final Object[] es = elements;
final E e = ArrayDeque.elementAt(es, ArrayDeque.dec(tail, es.length));
if (e == null) {
throw new NoSuchElementException();
}
return e;
}
  • 移除并返回队列头部元素:pollFirst、removeFirst【双向队列为空时抛出 NoSuchElementException 异常】
    /**
* 移除并返回双端队列的头部元素
*/
@Override
public E pollFirst() {
final Object[] es;
final int h;
// 读取头部元素
final E e = ArrayDeque.elementAt(es = elements, h = head);
if (e != null) {
// 如果元素存在,则将头部置为 null
es[h] = null;
// 循环递增头部索引
head = ArrayDeque.inc(h, es.length);
}
return e;
} /**
* @throws NoSuchElementException {@inheritDoc}
*/
@Override
public E removeFirst() {
final E e = pollFirst();
if (e == null) {
throw new NoSuchElementException();
}
return e;
}
  • 移除并返回队列尾部元素:pollLast、removeLast【双向队列为空时抛出 NoSuchElementException 异常】
    /**
* 移除并返回双端队列的尾部元素
*/
@Override
public E pollLast() {
final Object[] es;
final int t;
// 读取尾部元素
final E e = ArrayDeque.elementAt(es = elements, t = ArrayDeque.dec(tail, es.length));
if (e != null) {
// 如果存在,则将其置为 null,同时更新尾部索引
es[tail = t] = null;
}
return e;
} /**
* @throws NoSuchElementException {@inheritDoc}
*/
@Override
public E removeLast() {
final E e = pollLast();
if (e == null) {
throw new NoSuchElementException();
}
return e;
}

栈相关操作

  • 入栈
    /**
* 元素入栈
*/
@Override
public void push(E e) {
addFirst(e);
}
  • 出栈
    /**
* 元素出栈,如果栈为空,则抛出 NoSuchElementException 异常
*/
@Override
public E pop() {
return removeFirst();
}
  • 查看栈顶元素
    /**
* 查看单向队列/栈的头部元素
*/
@Override
public E peek() {
return peekFirst();
}
  • 栈长度
    /**
* 返回 ArrayDeque 中的元素个数
*/
@Override
public int size() {
return ArrayDeque.sub(tail, head, elements.length);
}
  • 是否为空
    /**
* ArrayDeque 是否为空
*/
@Override
public boolean isEmpty() {
return head == tail;
}

ArrayDeque 源码分析的更多相关文章

  1. 死磕 java集合之ArrayDeque源码分析

    问题 (1)什么是双端队列? (2)ArrayDeque是怎么实现双端队列的? (3)ArrayDeque是线程安全的吗? (4)ArrayDeque是有界的吗? 简介 双端队列是一种特殊的队列,它的 ...

  2. Android源码分析—深入认识AsyncTask内部机制

    本文转载http://blog.csdn.net/singwhatiwanna/article/details/17596225该博主博文,谢谢该博主提供的好文章! 前言 什么是AsyncTask,相 ...

  3. PriorityQueue源码分析

          PriorityQueue其实是一个优先队列,和先进先出(FIFO)的队列的区别在于,优先队列每次出队的元素都是优先级最高的元素.那么怎么确定哪一个元素的优先级最高呢,jdk中使用堆这么一 ...

  4. Android面试题-OkHttp3源码分析

    本文配套视频: okhttp内核分析配套视频一 okhttp内核分析配套视频二 okhttp内核分析配套视频三 源码分析相关面试题 Volley源码分析 注解框架实现原理 基本使用 从使用方法出发,首 ...

  5. 【转载】AsyncTask源码分析

    原文地址:https://github.com/white37/AndroidSdkSourceAnalysis/blob/master/article/AsyncTask%E5%92%8CAsync ...

  6. Android应用AsyncTask处理机制详解及源码分析

    1 背景 Android异步处理机制一直都是Android的一个核心,也是应用工程师面试的一个知识点.前面我们分析了Handler异步机制原理(不了解的可以阅读我的<Android异步消息处理机 ...

  7. 死磕 java集合之LinkedList源码分析

    问题 (1)LinkedList只是一个List吗? (2)LinkedList还有其它什么特性吗? (3)LinkedList为啥经常拿出来跟ArrayList比较? (4)我为什么把LinkedL ...

  8. Okhttp源码分析--基本使用流程分析

    Okhttp源码分析--基本使用流程分析 一. 使用 同步请求 OkHttpClient okHttpClient=new OkHttpClient(); Request request=new Re ...

  9. okHttp3 源码分析

    一, 前言 在上一篇博客OkHttp3 使用详解里,我们已经介绍了 OkHttp 发送同步请求和异步请求的基本使用方法. OkHttp 提交网络请求需要经过这样四个步骤: 初始化 OkHttpClie ...

随机推荐

  1. tcp和udp详解??

    TCP:面向连接的可靠传输 tcp规定了:传输服务必须建立连接      传输结束必须断开连接      传输数据必须保证可靠 数据的可靠性:无重复.无丢失.无失序.无差错. 建立连接(三次握手): ...

  2. osi七层模型??

    1.应用层:提供用户服务,例如处理应用程序,文件传输,数据管理      (HTTP.RTSP.FTP) 2.表示层:做数据的转换和压缩,加解密等 3.会话层:决定了进程间的连接建立,选择使用什么样的 ...

  3. Ubantu创建热点并共享——2019年5月10日更新

    只需要两步,参考以下两篇文章: ubuntu16.04上安装配置DHCP服务的详细过程 Ubuntu18.04 创建与编辑热点的方法

  4. 01分数规划问题(二分法与Dinkelbach算法)

    链接 前置技能 二分思想 最短路算法 一些数学脑细胞? 问题模型1基本01分数规划问题给定n个二元组(valuei,costi),valuei是选择此二元组获得的价值(非负),costi是选择此二元组 ...

  5. 理解PHP面向对象三大特性

    一.封装性 目的:保护类里面的数据,让类更安全, protected和private只能在类中或子类访问,通过public提供有限的接口供外部访问,封装是控制访问,而不是拒绝访问 封装关键字:publ ...

  6. Windows系统下安装MySQL详细教程(命令安装法)

    1.安装包下载. 下载地址:https://dev.mysql.com/downloads/mysql/ 点击下载之后,可以选择注册Oracle账号,也可以跳过直接下载. 下载完成后,选择一个磁盘内放 ...

  7. MySQL之表查询

    语法执行顺序 from >>>从那张表 where >>> 全局的筛选条件 group by>>> 必须是用在where 之后一组就是为了接下来我 ...

  8. Cockpit- Linux 服务器管理接口

    Cockpit- Linux 服务器管理接口 功能 它包含 systemd 服务管理器. 有一个用于故障排除和日志分析的 Journal 日志查看器. 包括 LVM 在内的存储配置比以前任何时候都要简 ...

  9. SVN更新报错:Checksum mismatch for 解决办法

    问题: Checksum mismatch while updating '……'; expected: '3f9fd4dd7d1a0304d8020f73300a3e07', actual: 'cd ...

  10. Educational Codeforces Round 68 (Rated for Div. 2) D. 1-2-K Game (博弈, sg函数,规律)

    D. 1-2-K Game time limit per test2 seconds memory limit per test256 megabytes inputstandard input ou ...