Arraydeque源码分析

ArrayDeque1.6

ArrayDeque是JDK容器中的一个双端队列实现,内部使用数组进行元素存储,不允许存储null值,可以高效的进行元素查找和尾部插入取出,是用作队列、双端队列、栈的绝佳选择,性能比LinkedList还要好,1.6引入

问题:为什么要用循环数组

​ 由于一般的数组实现的队列结构在频繁出队的情况下,会产生假溢出现象,导致数组使用效率降低,所以引入循环队列这种结构。

假溢出:在顺序队中,当尾指针已经到了数组的上界,不能再有入队操作,但其实数组中还有空位置。因为尾指针总会指向最后一个元素的下一个位置,当下一个位置为数组长度+1时,此时判断数组满,但是数组还有空位置,这就是假溢出现象,为了解决此问题引入了循环数组的概念

在这里插入图片描述

特性

  1. 无容量大小限制,容量按需增长;
  2. 非线程安全队列,无同步策略,不支持多线程安全访问;
  3. 当用作栈时,性能优于Stack,当用于队列时,性能优于LinkedList
  4. 两端都可以操作
  5. 具有fail-fast特征
  6. 不能存储null,因为tail总指向的空,如果存在空元素,将无法判断数组是否满
  7. 支持双向迭代器遍历

源码分析

继承和实现

/**
 * Resizable-array implementation of the {@link Deque} interface.  Array
 * deques have no capacity restrictions; they grow as necessary to support
 * usage.  They are not thread-safe; in the absence of external
 * synchronization, they do not support concurrent access by multiple threads.
 * Null elements are prohibited.  This class is likely to be faster than
 * {@link Stack} when used as a stack, and faster than {@link LinkedList}
 * when used as a queue.
 {@link Deque}接口的可调整数组实现。阵列
deques没有容量限制;它们根据需要增长以支持
用法。它们不是线程安全的;在没有外部
同步,它们不支持多线程并发访问。
禁止使用空元素。这节课可能比
{@link Stack}用作堆栈时,速度比{@link LinkedList}快
当用作队列时。
public class ArrayDeque<E> extends AbstractCollection<E>
                           implements Deque<E>, Cloneable, Serializable
  1. 继承AbstractCollection,而AbstractCollection继承Collection,里面实现集合的基本方法
  2. 实现Deque接口,双端队列
  3. Cloneable接口 标记接口,重写Object中的clone方法,如果不实现这个接口,则会抛出CloneNotSupportedException(克隆不被支持)异常
  4. Serializable接口 实现可序列化

属性

//数组
transient Object[] elements; // non-private to simplify nested class access非私有以简化嵌套类访问
transient int head; // 头部引用
transient int tail;//尾部引用
private static final int MIN_INITIAL_CAPACITY = 8;//初始大小,必须是2的幂次方

构造器

public ArrayDeque() {
    
     //无参构造 数组初始值大小为16
    elements = new Object[16]; 
}
public ArrayDeque(int numElements) {
    
     // 有参构造,因为底层是循环数组,必须满足是2的n次方幂,若不是2的n次方则会进行以下操作
        allocateElements(numElements);
    }
//分配
 private void allocateElements(int numElements) {
    
    
        elements = new Object[calculateSize(numElements)];
    }
//计算大小 分配合适大小的数组
private static int calculateSize(int numElements) {
    
    
        int initialCapacity = MIN_INITIAL_CAPACITY;// 8
        // Find the best power of two to hold elements.
        // Tests "<=" because arrays aren't kept full.
        //找到2的最佳幂。
        //测试“<=”,因为数组没有保持满。
        if (numElements >= initialCapacity) {
    
    //判断传入大小和初始大小的大小
            initialCapacity = numElements;
            // a|b的规则是 两个二进制对应位为0时该位为0,否则为1
            //此处设计思想为:
            //通过| 和 位操作 将原数值增大到最近的2的n次方数值
            // 比如5 0000 0101 最近的2的n次方为 2^3 8 
            // 第一次  0101 | 0010 = 0111——》 7
            initialCapacity |= (initialCapacity >>>  1);// /2
            //第二次  0111 | 0001 = 0111   7  以下一样
            initialCapacity |= (initialCapacity >>>  2);//  /4
            initialCapacity |= (initialCapacity >>>  4);//  /8
            initialCapacity |= (initialCapacity >>>  8);//  /16
            initialCapacity |= (initialCapacity >>> 16);//  /32
            //最后输出7 在进行++ 操作 即为8
            initialCapacity++;
           // 但是会出现如果传入值大于2^30 那经过转化后就会变为负值,此时会把初始值设置为2^30最大容量
            if (initialCapacity < 0)   // Too many elements, must back off
                initialCapacity >>>= 1;// Good luck allocating 2 ^ 30 elements
        }
        return initialCapacity;
    }
// 将集合c加入数组中
public ArrayDeque(Collection<? extends E> c) {
    
    
        allocateElements(c.size());
        addAll(c);
    }

方法

因为它大多数情况下都会用作栈和队列的使用,除了基本的Collection接口的操作外,还有针对栈和队列的操作,以下会介绍增删改查的源码分析

增加

需要注意的时:此处的add和offer add会判处异常,而offer不会抛出异常

public void addFirst(E e) {
    
     // 头部添加
    if (e == null)//误差分析
        throw new NullPointerException(); //抛出异常  没有指针异常
    //这里用用&操作,是存在head=0的情况 -1和elements.length - 1 & 的值为elements.length - 1,达到循环数组的操作
    elements[head = (head - 1) & (elements.length - 1)] = e;
    if (head == tail)//在这里的tail不是指的是队尾元素,而是尾元素的下一个空位置
        doubleCapacity();//此时head = tail 扩容看下边
}
 public void addLast(E e) {
    
     //尾部添加
        if (e == null)
            throw new NullPointerException();
        elements[tail] = e; //直接tail位置
     //如果tail=head,说明数组满,与要扩容
        if ( (tail = (tail + 1) & (elements.length - 1)) == head)
            doubleCapacity();
    }
     public boolean offerFirst(E e) {
    
     // 头部添加
        addFirst(e);
        return true;
    }
     public boolean offerLast(E e) {
    
     //尾部添加
        addLast(e);
        return true;
    }
     public boolean add(E e) {
    
    //当用作队列时  默认队尾添加
        addLast(e);
        return true;
    }
     public boolean offer(E e) {
    
     //队尾添加
        return offerLast(e);
    }
  public void push(E e) {
    
     //当栈用时,默认头部入栈
        addFirst(e);
    }

扩容处理,采用2倍扩容方式

此处的assert关键字 1.4引入,其意思为对某种假设条件进行检查,它被定义为宏的形式,不是函数,程序在假设条件下,能够正常良好的运作,其实就相当于一个 if 语句

if(假设成立)
{
    
    
     程序正常运行;
}
else
{
    
    
      报错&&终止程序!(避免由程序运行引起更大的错误)  
}
private void doubleCapacity() {
    
    
    assert head == tail; // 断言判断是否相等
    int p = head; // 保存head
    int n = elements.length; //保存数组长度
    int r = n - p; // number of elements to the right of p 保存p(head)右侧元素数
    int newCapacity = n << 1; // 2倍扩容
    if (newCapacity < 0) //可能超出2的30次方了
        throw new IllegalStateException("Sorry, deque too big");
    Object[] a = new Object[newCapacity]; //定义一个新数组
    //这里为什么要用两次arraycopy,因为要保证head还指向头部
    System.arraycopy(elements, p, a, 0, r);
    System.arraycopy(elements, 0, a, r, p);
    elements = a;
    head = 0; // 更新head指向头位置
    tail = n;//更新tail指向尾部元素的下一个
}

删除元素,首尾

此处的remove 和 poll 前者会抛出异常,后者不会

 public E remove() {
    
     // 当作队列时默认头部删除
        return removeFirst();
    }
 public E poll() {
    
     // 默认头部删除
        return pollFirst();
    }

public E removeFirst() {
    
    
    E x = pollFirst(); 
    if (x == null)//此时不存在会抛出异常
        throw new NoSuchElementException();//没有此元素异常
    return x;
}
 public E removeLast() {
    
    
        E x = pollLast();
        if (x == null)
            throw new NoSuchElementException();
        return x;
    }
     public E pollFirst() {
    
    
        int h = head; //保存头部
        @SuppressWarnings("unchecked") //未选中 告诉编译器忽略 unchecked 警告信息,意思可以是null元素,编译期会通过,因为deque接口不允许有空值
        E result = (E) elements[h]; //保存这个元素
        // Element is null if deque empty 如果deque为空,则元素为null
        if (result == null)
            return null;
        elements[h] = null;     // Must null out slot 垃圾回收
        head = (h + 1) & (elements.length - 1);//头指向指向下一个
        return result;//返回这个值
    }

    public E pollLast() {
    
    //尾部删除
        int t = (tail - 1) & (elements.length - 1);//因为tail指向的是最后一个元素的下一个空位置,所以得先找到最后一个元素
        @SuppressWarnings("unchecked")//可以是空
        E result = (E) elements[t];
        if (result == null)
            return null;
        elements[t] = null;
        tail = t;
        return result;
    }
     public E pop() {
    
    //当作栈时默认头部出栈
        return removeFirst();
    }

删除指定元素,为什么要进行多次安全检测,因为Arraydeque是非线程安全的

 // 删除第一次出现的元素
public boolean removeFirstOccurrence(Object o) {
    
    
    if (o == null)
        return false;
    int mask = elements.length - 1;//保存数组长度 mask即掩码
    int i = head;//保存头部
    Object x; //x用于保存待删的元素
    while ( (x = elements[i]) != null) {
    
    //从前往后遍历数组
        if (o.equals(x)) {
    
    //如果相等
            delete(i); //删除i
            return true;
        }
        i = (i + 1) & mask;
    }
    return false;
}
 public boolean removeLastOccurrence(Object o) {
    
    
        if (o == null)
            return false;
        int mask = elements.length - 1;
        int i = (tail - 1) & mask;
        Object x;
        while ( (x = elements[i]) != null) {
    
    //从后往前遍历数组
            if (o.equals(x)) {
    
    
                delete(i);
                return true;
            }
            i = (i - 1) & mask;
        }
        return false;
    }
    private void checkInvariants() {
    
     // 有效性检查,
        assert elements[tail] == null; // 判断是否尾部为空,tail位置没有元素
        //如果head == tail说明数组为空,令head位置为空,否则头部有元素,tail-1有元素
         assert head == tail ? elements[head] == null :
            (elements[head] != null &&
             elements[(tail - 1) & (elements.length - 1)] != null);
         //head-1位置没有元素
        assert elements[(head - 1) & (elements.length - 1)] == null;
    }

     private boolean delete(int i) {
    
     //删除 i位置元素
        checkInvariants();//校验不变量  有效性检查
        final Object[] elements = this.elements;//定义一个新数组保存旧数组
        final int mask = elements.length - 1;
        final int h = head;//保存头部
        final int t = tail;//保存尾部
        final int front = (i - h) & mask;//i位置前的元素个数
        final int back  = (t - i) & mask;//i位置后的元素个数

        // Invariant: head <= i < tail mod circularity
        //不变的是: 头小于i小于tail 保证循环性
         //再次检验,如果i到头部的距离大于等于尾部到头部的距离,表示当前队列已经被修改了,通过最开始检测,i是不应该满足该条件。
        if (front >= ((t - h) & mask))//如果i不在head和tail之间
            throw new ConcurrentModificationException();
         //判断i靠近头还是尾,尽量移动较少元素
        // Optimize for least element motion
        if (front < back) {
    
    //如果i靠近head
            if (h <= i) {
    
    //在进行检测 h小于等于i 在i前面
                //直接覆盖 比如 0 1 2 3 3是待删元素,覆盖后 0 0 1 2
                System.arraycopy(elements, h, elements, h + 1, front);
            } else {
    
     // Wrap around h大于i 在i后面
                System.arraycopy(elements, 0, elements, 1, i);
                elements[0] = elements[mask];
                System.arraycopy(elements, h, elements, h + 1, mask - h);
            }
            elements[h] = null;
            head = (h + 1) & mask;
            return false;//返回false则是 从左往右移
        } else {
    
     //i靠近tail
            if (i < t) {
    
     // Copy the null tail as well i在tail前
                System.arraycopy(elements, i + 1, elements, i, back);
                tail = t - 1;
            } else {
    
     // Wrap around  i在tail后面
                System.arraycopy(elements, i + 1, elements, i, mask - i);
                elements[mask] = elements[0];
                System.arraycopy(elements, 1, elements, 0, t);
                tail = (t - 1) & mask;
            }
            return true; //返回true则是从右往左
        }
    }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6IDsaiLa-1608379323651)(D:\笔记\Typora\JAVANSH\数据结构\quque和deque接口\image-20201218223914922.png)]

获取元素

public E element() {
    
    //获取元素 默认获取队头
    return getFirst();
}
public E getFirst() {
    
    
        @SuppressWarnings("unchecked")
        E result = (E) elements[head];
        if (result == null)
            throw new NoSuchElementException();
        return result;
    }
     public E getLast() {
    
    
        @SuppressWarnings("unchecked")
        E result = (E) elements[(tail - 1) & (elements.length - 1)];
        if (result == null)
            throw new NoSuchElementException();
        return result;
    }
     @SuppressWarnings("unchecked")
    public E peekFirst() {
    
    
        // elements[head] is null if deque empty
        return (E) elements[head];
    }

    @SuppressWarnings("unchecked")
    public E peekLast() {
    
    
        return (E) elements[(tail - 1) & (elements.length - 1)];
    }

是否包含contains

public boolean contains(Object o) {
    
    
    if (o == null)
        return false;
    int mask = elements.length - 1;
    int i = head;
    Object x;
    while ( (x = elements[i]) != null) {
    
    
        if (o.equals(x))
            return true;
        i = (i + 1) & mask;
    }
    return false;
}

迭代器

//向前迭代器
public Iterator<E> iterator() {
    
    
    return new DeqIterator();
}
private class DeqIterator implements Iterator<E> {
    
    
        private int cursor = head;
        private int fence = tail;
        private int lastRet = -1;//指向最近的next()调用返回的索引,可以找到需要删除的位置

        public boolean hasNext() {
    
    
            return cursor != fence; //终止条件 head = tail时
        }

        public E next() {
    
    
            if (cursor == fence)
                throw new NoSuchElementException();
            @SuppressWarnings("unchecked")
            E result = (E) elements[cursor]; // 保存当前值
            // This check doesn't catch all possible comodifications,
            // but does catch the ones that corrupt traversal
            //这个检查并没有捕捉到所有可能的修改,
            //但能抓到那些破坏遍历的
            if (tail != fence || result == null)//有效性检查
                throw new ConcurrentModificationException();
            lastRet = cursor;// 更新lastRet
            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() 如果将元素从右往左移,需要将游标减1。因为next时游标+1了
                cursor = (cursor - 1) & (elements.length - 1);//回退1
                fence = tail; //更新fence
            }
            lastRet = -1;//重置
        }

//向后迭代器
 public Iterator<E> descendingIterator() {
    
    //跟向前差不多
        return new DescendingIterator();
    }

实现栈

//判空
public boolean isEmpty() {
    
    
    return head == tail;
}
//出栈
 public E pop() {
    
    
        return removeFirst();
    }
//入栈
 public void push(E e) {
    
    
        addFirst(e);
    }
//获取栈顶元素
 public E peek() {
    
    
        return peekFirst();
    }

实现队列

public boolean add(E e) {
    
      
    addLast(e);  
    return true;  
}  
  
public boolean offer(E e) {
    
      
    return offerLast(e);  
}  
  
public E remove() {
    
      
    return removeFirst();  
}  
  
public E poll() {
    
      
    return pollFirst();  
}  
  
public E element() {
    
      
    return getFirst();  
}  
  
public E peek() {
    
      
    return peekFirst();  
}  

Collection集合方法

public int size() {
    
     //有效个数
    return (tail - head) & (elements.length - 1);
}
//判空
 public boolean isEmpty() {
    
    
        return head == tail;
    }
//迭代器
     public Iterator<E> iterator() {
    
    
        return new DeqIterator();
    }

    public Iterator<E> descendingIterator() {
    
    
        return new DescendingIterator();
    }
//是否包含
 public boolean contains(Object o) {
    
    
        if (o == null)
            return false;
        int mask = elements.length - 1;
        int i = head;
        Object x;
        while ( (x = elements[i]) != null) {
    
    
            if (o.equals(x))
                return true;
            i = (i + 1) & mask;
        }
        return false;
    }
//转数组
 public Object[] toArray() {
    
    
        return copyElements(new Object[size()]);
    }
  private <T> T[] copyElements(T[] a) {
    
    
        if (head < tail) {
    
    
            System.arraycopy(elements, head, a, 0, size());
        } else if (head > tail) {
    
    
            int headPortionLen = elements.length - head;
            System.arraycopy(elements, head, a, 0, headPortionLen);
            System.arraycopy(elements, 0, a, headPortionLen, tail);
        }
        return a;
    }

猜你喜欢

转载自blog.csdn.net/weixin_46078315/article/details/111409545