zoukankan      html  css  js  c++  java
  • jdk1.8-ArrayDeque

    一:类的继承关系
    UML图

    类的继承关系:
    public class ArrayDeque<E> extends AbstractCollection<E>
    implements Deque<E>, 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 <i>front</i> 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<? extends E> c) {
    allocateElements(c.size());
    addAll(c);
    }
    分析:初始化数组长度为大于等于集合长度的最小2次幂,调用addAll()方法

    /**
    * {@inheritDoc}
    *
    * <p>This implementation iterates over the specified collection, and adds
    * each object returned by the iterator to this collection, in turn.
    *
    * <p>Note that this implementation will throw an
    * <tt>UnsupportedOperationException</tt> unless <tt>add</tt> 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<? extends E> 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.
    *
    * <p>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

    假设我们有这么一个队列:
    现在数组下标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是从原数组的头部右边数据开始复制的(包括头部),为什么不从原数组头部左边开始复制数据呢?仔细想下,是不是有点奇怪?
    其实我们看下图就知道了,
    还是这个例子:
    其实循环队列的起始位置,就是head所在位置,也就是9所在的位置,它都叫队头了,当然就是起始位置了,扩容后新数组应该为:

    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



    有疑问,扫我二维码添加微信,欢迎骚扰!
    坚持做一件事,一起学习。


  • 相关阅读:
    matplotlib数据可视化之柱形图
    xpath排坑记
    Leetcode 100. 相同的树
    Leetcode 173. 二叉搜索树迭代器
    Leetcode 199. 二叉树的右视图
    Leetcode 102. 二叉树的层次遍历
    Leetcode 96. 不同的二叉搜索树
    Leetcode 700. 二叉搜索树中的搜索
    Leetcode 2. Add Two Numbers
    Leetcode 235. Lowest Common Ancestor of a Binary Search Tree
  • 原文地址:https://www.cnblogs.com/lizb0907/p/10478472.html
Copyright © 2011-2022 走看看