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. Python学习-26.Python中的三角函数

    Python中的三角函数位于math模块内. 引入模块: import math 输出pi import math print(math.pi) 得:3.141592653589793 math模块内 ...

  2. Python学习-22.Python中的函数——type

    type函数可以检测任何值或变量的类型. 例子: def printType(var): print(type(var)) class TestClass: pass printType(1) pri ...

  3. 一起学习MVC(2)Global.asax的学习

    在Global.asax.cs文件中        protected void Application_BeginRequest(Object sender, EventArgs e)  {     ...

  4. C#全局键盘监听(Hook)

    一.为什么需要全局键盘监听? 在某些情况下应用程序需要实现快捷键执行特定功能,例如大家熟知的QQ截图功能Ctrl+Alt+A快捷键,只要QQ程序在运行(无论是拥有焦点还是处于后台运行状态),都可以按下 ...

  5. 截图-----Selenium快速入门(十二)

    在自动化测试过程中,截图是常见的操作,因为有时候单靠程序无法判断是否已得到期望的结果,所以需要截图判断.又或者截图是作为判断的存证.Selenium的截图操作也是非常简单,而且自带了一个文件操作类Fi ...

  6. vs2017新建.netcore相关项目提示"未检测到任何.NET Core SDK"或打开.net core 相关项目Web层总是未能正常加载

    近来vs2017出现一个非常怪的现象,之前新建.net core相关项目好好的,现在出现问题,如下: 解决办法,是更新vs2017,界面如下:

  7. php重写session的存储机制

    重写session的存储机制 Session数据区 默认以 文件的形式存储与服务器操作系统临时目录中! 当 session数据区过多时,文件形式的存储,操作速度变慢.磁盘的读写(IO,input/ou ...

  8. 在.net Core 使用PDF模板文件生成PDF文件,代替WEB打印控件!

    这几天找WEB打印控件,要么收费的,要么免费的只能在IE里用! 我只想简单的打个标签纸!百度2天,看到一老兄说可以用PDF,然后又开始百度..找到了一篇文章 http://www.jianshu.co ...

  9. 基于EasyUi的datagrid合并单元格JS写法

    $('#dg').datagrid({ width: 'auto', height: 'auto', scrollbarSize: , queryParams: {}, url: 'kkkk', co ...

  10. Netty 源码中对 Redis 协议的实现

    原文地址: haifeiWu的博客 博客地址:www.hchstudio.cn 欢迎转载,转载请注明作者及出处,谢谢! 近期一直在做网络协议相关的工作,所以博客也就与之相关的比较多,今天楼主结合 Re ...