Queue接口

 public abstract boolean add(E paramE);
public abstract boolean offer(E paramE); // 加入元素
public abstract E remove();
public abstract E poll(); // 获取元素

Queue接口提供了以上几个方法。

Queue使用时要尽量避免Collection的add()和remove()方法,而是要使用offer()来加入元素,使用poll()来获取并移出元素。

它们的优点是通过返回值可以判断成功与否,add()和remove()方法在失败的时候会抛出异常。

如果要使用前端而不移出该元素,使用element()或者peek()方法。

ArrayDeque

ArrayDeque的特点

  • 大小自增长的队列
  • 内部使用数组存储数据
  • 线程不安全
  • 内部数组长度为8、16、32….. 2的n次方
  • 头指针head从内部数组的末尾开始,尾指针tail从0开始,在头部插入数据时,head减一,在尾部插入数据时,tail加一。当head==tail时说明数组的容量满足不了当前的情况,此时需要扩大容量为原来的二倍。

核心思想图

代码解析

  • 分配数组大小
 private void allocateElements(int numElements) {
int initialCapacity = MIN_INITIAL_CAPACITY;
// 如果指定的数组长度大于最小的值,需要计算数组大小。
// 算法利用或运算和右移运算,计算结果始终为2的n次方,比如numElements的值为4、5、6、7时,initialCapacity的结果为8;numElements的值为8、9、10、11、12、13、14、15时,initialCapacity的结果为16。
// 设计数组的长度为2的n次方是为循环链表考虑的,后面会解释。
if (numElements >= initialCapacity) {
initialCapacity = numElements;
initialCapacity |= (initialCapacity >>> 1);
initialCapacity |= (initialCapacity >>> 2);
initialCapacity |= (initialCapacity >>> 4);
initialCapacity |= (initialCapacity >>> 8);
initialCapacity |= (initialCapacity >>> 16);
initialCapacity++;
// 如果值太大溢出(即值小于0),需要缩小2倍
if (initialCapacity < 0) // Too many elements, must back off
initialCapacity >>>= 1;// Good luck allocating 2 ^ 30 elements
}
elements = new Object[initialCapacity];
}
  • 扩大数组长度
 private void doubleCapacity() {
// assert head == tail;
int p = head;
int n = elements.length;
// 头指针右边的长度
int r = n - p;
// 扩大2倍
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;
// 初始化头指针为0
head = 0;
// 初始化尾指针为n
tail = n;
}
  • 在头部插入数据
 public void addFirst(E e) {
if (e == null)
throw new NullPointerException("e == null");
// 在头部插入数据,头指针向左移动1,即减一,elements.length为2的n次方,所以elements.lenght-1的二进制表示为1111....1111。
// 特殊情况:假设数组的长度为16,初始化时head为0,那么(head - 1) & (elements.length - 1)就是-1 & 15的值,负数的二进制为对应正数的二进制的补码,-1即为11111....1111,那么-1 & 15 = 15,如上图,当首次向队列头部插入数据时,head在数组的末尾。
elements[head = (head - 1) & (elements.length - 1)] = e;
// 头指针和尾指针相等时(即相遇),表示当前的数组长度不够,需要扩大数组长度
if (head == tail)
doubleCapacity();
}
  • 在尾部插入数据
 public void addLast(E e) {
if (e == null)
throw new NullPointerException("e == null");
elements[tail] = e;
// tail向右移动,如果头指针和尾指针相等时(即相遇),表示当前的数组长度不够,需要扩大数组长度
if ( (tail = (tail + 1) & (elements.length - 1)) == head)
doubleCapacity();
}
  • 遍历
 private class DeqIterator implements Iterator<E> {
// 从头指针位置开始遍历
private int cursor = head;
private int fence = tail;
private 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;
// 每次加一,利用与运算,形成循环。比如,数组的长度为16,当cursor递增到15时,下面的计算应为16 & 15 = 0,cursor的值变为了0.
cursor = (cursor + 1) & (elements.length - 1);
return result;
} public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
if (delete(lastRet)) { // if left-shifted, undo increment in next()
cursor = (cursor - 1) & (elements.length - 1);
fence = tail;
}
lastRet = -1;
}
}

有以下几点总结:

1)ArrayDeque有两个类属性,head和tail,两个指针。

2)ArrayDeque通过一个数组作为载体,其中的数组元素在add等方法执行时不移动,发生变化的只是head和tail指针,而且指针是循环变化,数组容量不限制。

3)offer方法和add方法都是通过其中的addLast方法实现,每添加一个元素,就把元素加到数组的尾部,此时,head指针没有变化,而tail指针加一,因为指针是循环加的,

所以当tail追上head((this.tail = this.tail + 1 & this.elements.length - 1) == this.head)时,数组容量翻一倍,继续执行。

4)remove方法和poll方法都是通过其中的pollFirst方法实现,每移除一个元素,该元素所在位置变成null,此时,tail指针没有变化,而head指针加一,当数组中没有数据时,返回null。

5)因为ArrayDeque不是线程安全的,所以,用作堆栈时快于 Stack,在用作队列时快于 LinkedList。

ArrayDeque解析的更多相关文章

  1. 给jdk写注释系列之jdk1.6容器(11)-Queue之ArrayDeque源码解析

    前面讲了Stack是一种先进后出的数据结构:栈,那么对应的Queue是一种先进先出(First In First Out)的数据结构:队列.      对比一下Stack,Queue是一种先进先出的容 ...

  2. Java容器解析系列(7) ArrayDeque 详解

    ArrayDeque,从名字上就可以看出来,其是通过数组实现的双端队列,我们先来看其源码: /** 有自动扩容机制; 不是线程安全的; 不允许添加null; 作为栈使用时比java.util.Stac ...

  3. Android AsyncTask 源码解析

    1. 官方介绍 public abstract class AsyncTask extends Object  java.lang.Object    ↳ android.os.AsyncTask&l ...

  4. Android AsyncTask完全解析,带你从源码的角度彻底理解

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/11711405 我们都知道,Android UI是线程不安全的,如果想要在子线程里进 ...

  5. 给jdk写注释系列之jdk1.6容器(12)-PriorityQueue源码解析

    PriorityQueue是一种什么样的容器呢?看过前面的几个jdk容器分析的话,看到Queue这个单词你一定会,哦~这是一种队列.是的,PriorityQueue是一种队列,但是它又是一种什么样的队 ...

  6. AsyncTask 解析

    [转载自 http://blog.csdn.net/yanbober ] 1 背景 Android异步处理机制一直都是Android的一个核心,也是应用工程师面试的一个知识点.前面我们分析了Handl ...

  7. Android -- AsyncTask源码解析

    1,前段时间换工作的时候,关于AsyncTask源码这个点基本上大一点的公司都会问,所以今天就和大家一起来总结总结.本来早就想写这篇文章的,当时写<Android -- 从源码解析Handle+ ...

  8. Android源码解析——AsyncTask

    简介 AsyncTask 在Android API 3引入,是为了使UI线程能被正确和容易地使用.它允许你在后台进行一些操作,并且把结果带到UI线程中,而不用自己去操纵Thread或Handler.它 ...

  9. 沉淀再出发:java中线程池解析

    沉淀再出发:java中线程池解析 一.前言 在多线程执行的环境之中,如果线程执行的时间短但是启动的线程又非常多,线程运转的时间基本上浪费在了创建和销毁上面,因此有没有一种方式能够让一个线程执行完自己的 ...

随机推荐

  1. 【PAT Advanced Level】1014. Waiting in Line (30)

    简单模拟题,注意读懂题意就行 #include <iostream> #include <queue> using namespace std; #define CUSTOME ...

  2. 动态的把固定格式的json数据以菜单形式插入

    var root=$("#side-menu"); $(menuData).each(function(i,n){ var top1Li=$("<li>< ...

  3. 团队项目第六周——Alpha阶段项目复审

    排名 队名 项目名 优点 缺点 1 大猪蹄子队 四六级背单词游戏 功能开发出来了,界面简洁美观. 功能的确开发出来了,但是还未完成整个程序.不过考虑到开发时长问题,可以理解.页面还是比较简洁,但是测试 ...

  4. 【Win10】实现 ListViewBase 平滑滚动

    首先解释下标题的 ListViewBase 是什么鬼.ListViewBase 我们可以查阅 MSDN 文档:https://msdn.microsoft.com/zh-cn/library/wind ...

  5. MLLib实践Naive Bayes

    引言 本文基于Spark (1.5.0) ml库提供的pipeline完整地实践一次文本分类.pipeline将串联单词分割(tokenize).单词频数统计(TF),特征向量计算(TF-IDF),朴 ...

  6. ServiceBase.OnStart 方法

    msdn 解释 派生类中实现时,在由服务控制管理器 (SCM) 或在操作系统启动时 (对于自动启动的服务) 时,将启动命令发送到服务时执行. 指定当服务启动时要执行的操作. 命名空间:   Syste ...

  7. CTFcrackTools-V3 – 一款旨在帮助 CTFer 在 CTF 中发挥作用的一个框架

    CTFcrackTools-V3 CTFcrackTools重置版 作者:米斯特安全-林晨.摇摆.奶权 米斯特安全团队首页:http://www.hi-ourlife.com/ 部分插件来源:希望团队 ...

  8. 【mongodb】——常用命令大全

    MongoDB是NoSQL数据库系统中比较流行的数据库之一.它也是最接近关系型数据库的,一个数据库可以包含多个集合(Collection),类似于关系数据库中的表:而每个集合中可以存储一组由列标识的记 ...

  9. n层满k叉树总共有多少个节点

    2叉树 1 3 7 对应公式为(2^n-1)/1 3叉树 1 4 13 对应公式为(3^n-1)/2 4叉树 1 5 21对应公式为(4^n-1)/3 ... n层k叉树,总共有(k^n-1)/k-1 ...

  10. 小div在大div里面水平垂直都居中的实现方法

    关于如何设置小盒子在大盒子里面水平垂直方向同时居中的实现方法有很多种,下面仅列举了常用的几种. 首先看一下要实现的效果图及对应的html代码: <div class="parent&q ...