JDK之ArrayDeque源码解析

刚入java不久的程序猿,对于简单的使用已毫不满足,最终为了一探究竟,翻开了JDK的源码,以下观点为自己的理解及看了多篇博客的总结,欢迎各位大神指出不对的地方,当然也欢迎和我一样刚学的同学,一起加油努力吧~~

ArrayDeque是什么

ArrayDeque是什么?deque英文翻译为双向队列,ArrayDeque就是实现了Deque接口的一个双向队列的集合,ArrayDeque也是由数组实现,数组的任意位置都可以作为头或者尾,ArrayDeque不允许元素为null

ArrayDeque源码解析

在上面我们了解到了ArrayDeque是什么,大致在脑海中留下初步的印象,接下来我们来看一下源码,由于Deque继承了Queue接口,这里我们就先来看下这个接口里定义的方法

public interface Queue<E> extends Collection<E> {
    /**
     * 插入元素,成功返回true,失败抛出异常
     */
    boolean add(E e);

    /**
     * 插入元素,成功返回true,失败返回false
     */
    boolean offer(E e);

    /**
     * 获取并删除队首元素,队列为空则抛出异常
     */
    E remove();

    /**
     * 获取并删除队首元素,队列为空是返回null
     */
    E poll();

    /**
     * 获取但不删除队首元素,队列为空抛出异常
     */
    E element();

    /**
     * 获取但不删除队首元素,队列为空返回null
     */
    E peek();
}

这里的方法已经注释过,大家可以自己看下每个方法的作用,还有就是Queue是继承了Collection接口的,说明最终扔是以迭代的方式来完成的,好了,我们在大致看下Deque接口定义的方法

public interface Deque<E> extends Queue<E> {
    /**
     * 在队列最前端插入元素,无返回值
     */
    void addFirst(E e);

    /**
     * 在队列最后端插入元素,无返回值
     */
    void addLast(E e);

    /**
     * 在队列最前端插入元素,成功返回true失败返回false
     */
    boolean offerFirst(E e);

    /**
     * 在队列最后端插入元素,成功返回true失败返回false
     */
    boolean offerLast(E e);

    /**
     * 获取并删除队列最前端元素,队列为空抛出异常
     */
    E removeFirst();

    /**
     * 获取并删除队列最后端元素,队列为空抛出异常
     */
    E removeLast();

    /**
     * 获取并删除队列最前端元素,队列为空返回null
     */
    E pollFirst();

    /**
     * 获取并删除队列最后端元素,队列为空返回null
     */
    E pollLast();

    /**
     * 获取但不删除队列最前端元素,队列为空抛出异常
     */
    E getFirst();

    /**
     * 获取但不删除队列最后端元素,队列为空抛出异常
     */
    E getLast();

    /**
     * 获取但不删除队列最前端元素,队列为空返回null
     */
    E peekFirst();

    /**
     * 获取但不删除队列最后端元素,队列为空返回null
     */
    E peekLast();

    /**
     * 删除队列里第一次出现的指定元素
     */
    boolean removeFirstOccurrence(Object o);

    /**
     * 删除队列里最后一次出现的指定元素
     */
    boolean removeLastOccurrence(Object o);

    // *** Queue methods ***

    boolean add(E e);

    boolean offer(E e);

    E remove();

    E poll();

    E element();

    E peek();

    // *** Stack methods ***

    /**
     * 内存空间允许的情况下,将元素放入出现在栈中的Dqueue中
     * 成功返回true,失败抛出异常
     */
    void push(E e);

    /**
     * 删除并返回这个队列的第一个元素
     */
    E pop();


    // *** Collection methods ***

    /**
     * 删除某个元素
     */
    boolean remove(Object o);

    /**
     * 是否包含某个元素
     */
    boolean contains(Object o);

    /**
     * 元素数量
     */
    public int size();

    /**
     * 迭代
     */
    Iterator<E> iterator();

    /**
     * 返回一个顺序反转后的迭代器
     */
    Iterator<E> descendingIterator();
}

上面就是Deque接口里的方法,看起来方法很多,其实方法无非就是添加,删除,取值,只不过添加删除头部还是尾部以及返回值的不同而已,整个体系看完,接着看最主要的,ArrayDque的源码

public class ArrayDeque<E> extends AbstractCollection<E>
                           implements Deque<E>, Cloneable, Serializable{
...
}

上面就是ArrayDeque的继承与实现,接下来看一下定义的变量

    /**
     * 存取队列元素的数组
     */
    private transient E[] elements;

    /**
     * 队列的头
     */
    private transient int head;

    /**
     * 队列的尾
     */
    private transient int tail;

    /**
     * 创建新的队列时最小容量
     */
    private static final int MIN_INITIAL_CAPACITY = 8;

这里队列的头与尾,可以说数组中任意位置都可以为头或者尾,继续看代码

    /**
     * 无参构造,初始化大小为默认大小16
     */
    public ArrayDeque() {
        elements = (E[]) new Object[16];
    }

    /**
     * 有参构造,参数为元素数量
     */
    public ArrayDeque(int numElements) {
        allocateElements(numElements);
    }

    /**
     * 有参构造,参数为集合
     */
    public ArrayDeque(Collection<? extends E> c) {
        allocateElements(c.size());
        addAll(c);
    }

    /**
     * 初始化数组
     */
    private void allocateElements(int numElements) {
        //初始化数组的最小容量
        int initialCapacity = MIN_INITIAL_CAPACITY;
        //找到大于numElements的最小的2的幂整数
        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 = (E[]) new Object[initialCapacity];
    }

上面是ArrayDeque的构造方法,感觉没什么特殊的东西,就是初始化数组,只不过一个是默认的16长度,一个是传入的参数,最后一个构造在初始化数组后,调用AbstractCollection的addAll方法将集合放入,继续看几个比较重要的方法

    /**
     * 扩容方法,当数组满了进行扩容操作,大小为原来的两倍
     */
    private void doubleCapacity() {
        assert head == tail;
        int p = head;
        //元素个数
        int n = elements.length;
        int r = n - p; // number of elements to the right of p
        //将容量扩大一倍
        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 = (E[])a;
        head = 0;
        tail = n;
    }

    /**
     * 在队列的前端添加元素,无返回值
     */
    public void addFirst(E e) {
        if (e == null)
            throw new NullPointerException();
        elements[head = (head - 1) & (elements.length - 1)] = e;
        //判断头尾是否重合,重合即数组满了,需进行扩容
        if (head == tail)
            doubleCapacity();
    }

    /**
     * 在队列的后端添加元素,无返回值
     */
    public void addLast(E e) {
        if (e == null)
            throw new NullPointerException();
        elements[tail] = e;
        //判断头尾是否重合,重合即数组满了,需进行扩容
        if ( (tail = (tail + 1) & (elements.length - 1)) == head)
            doubleCapacity();
    }

    /**
     * 在队列的前端添加元素,调用addFirst方法,返回值true
     */
    public boolean offerFirst(E e) {
        addFirst(e);
        return true;
    }

    /**
     * 在队列的后端添加元素,调用addLast方法,返回值true
     */
    public boolean offerLast(E e) {
        addLast(e);
        return true;
    }

    /**
     * 获取并删除最前端元素
     */
    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;
        //队列空为则元素为空
        E result = elements[h]; 
        if (result == null)
            return null;
        //head处置空    
        elements[h] = null; 
        //处理特殊情况,当h为elements.length-1时的情况
        head = (h + 1) & (elements.length - 1);
        return result;
    }

    public E pollLast() {
        //处理特殊情况,当tail为0时的情况
        int t = (tail - 1) & (elements.length - 1);
        E result = elements[t];
        if (result == null)
            return null;
        elements[t] = null;
        //下一个需要添加的下标
        tail = t;
        return result;
    }

    /**
     * 获取但不删除队列最前端元素
     */
    public E getFirst() {
        E x = elements[head];
        //队列为空抛出异常
        if (x == null)
            throw new NoSuchElementException();
        return x;
    }

    /**
     * 获取但不删除队列最后端元素
     */
    public E getLast() {
        E x = elements[(tail - 1) & (elements.length - 1)];
        //队列为空抛出异常
        if (x == null)
            throw new NoSuchElementException();
        return x;
    }

    /**
     * 获取但不删除队列最前端元素,队列为空返回null
     */
    public E peekFirst() {
        return elements[head]; 
    }

    /**
     * 获取但不删除队列最后端元素,队列为空返回null
     */
    public E peekLast() {
        return elements[(tail - 1) & (elements.length - 1)];
    }

好了,这里列出了ArrayDeque最主要的一些方法,其他方法大多都比较简单了,大家可以自己去翻一下源码,最后我们需要注意的是ArrayDeque存元素时,不能存入null元素,否则会报空指针异常,好了,ArrayDeque的解析到这里就结束了

发布了22 篇原创文章 · 获赞 11 · 访问量 1万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章