博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
jdk1.8-ArrayDeque
阅读量:7239 次
发布时间:2019-06-29

本文共 7505 字,大约阅读时间需要 25 分钟。

一:类的继承关系
UML图
1549337-20190316155922841-731838491.png
类的继承关系:
public class ArrayDeque
extends AbstractCollection
implements Deque
, Cloneable, Serializable
分析:继承自抽象结合类AbstractCollection
           实现超级队列Deque,克隆接口Cloneable, 序列化接口Serializable
二:看下类的成员属性
/**  * The array in which the elements of the deque are stored.  * The capacity of the deque is the length of this array, which is  * always a power of two. The array is never allowed to become  * full, except transiently within an addX method where it is  * resized (see doubleCapacity) immediately upon becoming full,  * thus avoiding head and tail wrapping around to equal each  * other.  We also guarantee that all array cells not holding  * deque elements are always null.  *  * 存放元素的Object[]数组(底层数据结构)  */ transient Object[] elements; // non-private to simplify nested class access
/**  * The index of the element at the head of the deque (which is the  * element that would be removed by remove() or pop()); or an  * arbitrary number equal to tail if the deque is empty.  *  * 队首元素所在位置  */ transient int head;
/**  * The index at which the next element would be added to the tail  * of the deque (via addLast(E), add(E), or push(E)).  *  * 队尾元素所在位置  */ transient int tail;
/**  * The minimum capacity that we'll use for a newly created deque.  * Must be a power of 2.  *  * 容量最小值,2的次幂,默认为8  */ private static final int MIN_INITIAL_CAPACITY = 8;
三:接着先认识下allocateElements(int numElements)方法
/**  * Allocates empty array to hold the given number of elements.  *  * @param numElements  the number of elements to hold  *  * 寻找numElements的最近的二次幂值initialCapacity  *  new一个长度为initialCapacity的新Object[]数组  */ private void allocateElements(int numElements) {
int initialCapacity = MIN_INITIAL_CAPACITY; // Find the best power of two to hold elements. // Tests "<=" because arrays aren't kept full. 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 } elements = new Object[initialCapacity]; }
分析:这个计算方法参考下面这个链接,讲的挺好
四:几个构造函数
1.
/**  * Constructs an empty array deque with an initial capacity  * sufficient to hold 16 elements.  *  * 无参构造函数初始化数组长度为16  */ public ArrayDeque() {
elements = new Object[16]; }
分析:无参构造方法,初始化一个长度为16的数组
2.
/**  * Constructs an empty array deque with an initial capacity  * sufficient to hold the specified number of elements.  *  * @param numElements  lower bound on initial capacity of the deque  *  * 传入长度,构造函数  */ public ArrayDeque(int numElements) {
allocateElements(numElements); }
分析:传入长度,初始为数组长度为大于等于numElements最小2次幂
3.
/**  * Constructs a deque containing the elements of the specified  * collection, in the order they are returned by the collection's  * iterator.  (The first element returned by the collection's  * iterator becomes the first element, or front of the  * deque.)  *  * @param c the collection whose elements are to be placed into the deque  * @throws NullPointerException if the specified collection is null  *  * 传入集合,构造函数  */ public ArrayDeque(Collection
c) {
allocateElements(c.size()); addAll(c); }
分析:初始化数组长度为大于等于集合长度的最小2次幂,调用addAll()方法
/**  * {
@inheritDoc} * *

This implementation iterates over the specified collection, and adds * each object returned by the iterator to this collection, in turn. * *

Note that this implementation will throw an * UnsupportedOperationException unless add is * overridden (assuming the specified collection is non-empty). * * @throws UnsupportedOperationException {

@inheritDoc} * @throws ClassCastException {
@inheritDoc} * @throws NullPointerException {
@inheritDoc} * @throws IllegalArgumentException {
@inheritDoc} * @throws IllegalStateException {
@inheritDoc} * * @see #add(Object) */ public boolean addAll(Collection
c) {
boolean modified = false; for (E e : c) if (add(e)) modified = true; return modified; }

分析:遍历集合,一个个add添加
五:最常用的方法
1.offerLast() 向队尾添加一个元素
/**  * Inserts the specified element at the end of this deque.  *  * @param e the element to add  * @return {
@code true} (as specified by {
@link Deque#offerLast}) * @throws NullPointerException if the specified element is null * * 添加到队尾 */ public boolean offerLast(E e) {
addLast(e); return true; }
分析:传入元素,调用addLast()方法
/**  * Inserts the specified element at the end of this deque.  *  * 

This method is equivalent to {

@link #add}. * * @param e the element to add * @throws NullPointerException if the specified element is null * */ public void addLast(E e) {
//判空 if (e == null) throw new NullPointerException(); //根据下标,队尾值为e elements[tail] = e; if ( (tail = (tail + 1) & (elements.length - 1)) == head) doubleCapacity(); }

分析:这里直接在队尾里添加了元素e,因为ArrayDeque是一个循环队列,所以当队尾和队头重合说明,队列满了,需要进行扩容。
这里要看下这个“&”按位与操作,只有1与上1才得1。
tail = (tail + 1) & (elements.length - 1)) == head
假设我们有这么一个队列:
1549337-20190316155923428-56410293.png
现在数组下标1、2、3都填满了数字,当我们在队尾tail里赋上8。这时候我们需要判断当前循环队列是否已满
下一个可以插入元素下标应该为:
tail = tail + 1 = 0 + 1 = 1
数组都是2的次幂,所以数组长度 - 1有个规律,从最高为起每一位都是1,例如:
当前leng - 1 = 3,用二进制表示为 0011
与操作
         0001
         0011
等于:0001 
即下标为1,此时与head下标相同,表明数组已满。需要扩容。
如果tail + 1是正数,与操作,相当于取余“%”,例如:我们这个1 % 3 = 1,取余后得到数刚好就是tail的索引下标。
如果是负数的话,这里与操作会得到负数本身。例如在头部添加方法addFrist():
public void addFirst(E e) {
if (e == null) throw new NullPointerException(); elements[head = (head - 1) & (elements.length - 1)] = e; if (head == tail) doubleCapacity(); }
分析:这里分析类似
接下来我们看:扩容操作
/**  * Doubles the capacity of this deque.  Call only when full, i.e.,  * when head and tail have wrapped around to become equal.  */ private void doubleCapacity() {
//断言头和尾是不是重,也就是队列是否满了 assert head == tail; //头部索引p int p = head; //队列长度n int n = elements.length; //头部元素右边有多少个元素r包括头部 int r = n - p; // number of elements to the right of p //队列长度扩大为2倍 int newCapacity = n << 1; //如果扩大两倍后长度超过了int大小变为负数,则抛错 if (newCapacity < 0) throw new IllegalStateException("Sorry, deque too big"); //new一个新的数组长度为原来的两倍 Object[] a = new Object[newCapacity]; //旧数组elements 从p头部开始, 新数组a,从0开始, 长度为r。所以是拷贝原数组p右侧数据包括p System.arraycopy(elements, p, a, 0, r); //旧数组elements 从下标0开始, 新数组a,从下标r开始, 长度为p。所以是拷贝原数组p左侧数据 System.arraycopy(elements, 0, a, r, p); //旧数组等于新数组a elements = a; //队头索引等于0 head = 0; //队尾索引等于n,n为旧数组的长度不是最新的数组 tail = n; }
分析:这里看上面的注释已经将得很清晰了,但是我们这里需要注意一个问题
在数组复制的过程,新数组下标0是从原数组的头部右边数据开始复制的(包括头部),为什么不从原数组头部左边开始复制数据呢?仔细想下,是不是有点奇怪?
其实我们看下图就知道了,
1549337-20190316155923883-1267179105.png
还是这个例子:
其实循环队列的起始位置,就是head所在位置,也就是9所在的位置,它都叫队头了,当然就是起始位置了,扩容后新数组应该为:
1549337-20190316155924607-216396388.png
2.pollFirst()从头部取出一个元素
public E pollFirst() {
//头部索引等于h int h = head; @SuppressWarnings("unchecked") //检查头部是否有元素 E result = (E) elements[h]; // Element is null if deque empty if (result == null) return null; //令当前位置为null elements[h] = null; // Must null out slot //队头索引向递增方向退一位 head = (h + 1) & (elements.length - 1); return result; }
到此我们就大概分析了下ArrayDeque
有疑问,扫我二维码添加微信,欢迎骚扰!
坚持做一件事,一起学习。
1549337-20190316155925590-274850678.jpg

转载于:https://www.cnblogs.com/lizb0907/p/10478472.html

你可能感兴趣的文章
CSS-垂直|水平居中问题的解决方法总结
查看>>
Oracle数据库常用命令记录
查看>>
产品开发 - 产品架构
查看>>
一行代码让谷歌浏览器变成在线编辑器
查看>>
Redis的C语言客户端(hiredis)的安装和使用
查看>>
Java 之Object 类
查看>>
Android 调用系统联系人界面的添加联系人,添加已有联系人,编辑和修改。
查看>>
C语言单向链表
查看>>
【LeedCode】3Sum
查看>>
基于Token的WEB后台认证机制
查看>>
hdu1412
查看>>
ssh卡在debug1: SSH2_MSG_KEXINIT sent解决方法
查看>>
HTTP 错误 404.17 - Not Found和 HTTP 错误 404.2 - Not Found 解决办法
查看>>
Python中可迭代对象、迭代器以及iter()函数的两个用法详解
查看>>
C# winform程序防止前台卡死
查看>>
JdbcDaoSupport应用
查看>>
Java加密算法(五)——非对称加密算法的由来DH
查看>>
centos7下vim8.1的编译安装教程
查看>>
服务器上传文件:通过远程桌面传输文件
查看>>
Spring MVC 教程,快速入门,深入分析
查看>>