美文网首页
Java集合之ArrayDeque

Java集合之ArrayDeque

作者: 苦寒行 | 来源:发表于2020-04-23 01:06 被阅读0次

    在总集篇中我大概梳理了一下整个集合类的关系,这篇文章是对总集篇的扩展,本文将详细讨论ArrayDeque的实现原理。所有涉及到的源码都是基于JDK11。顺便插一句,大家要学就学新的嘛,毕竟11可是长期支持版本,可以用好几年的那种。

    ArrayDeque简介

    ArrayDeque是基于数组实现的双端循环队列,通常来说,ArrayDeque可以实现队列的功能,也能实现栈的功能,同时可以通过双端的乱序操作实现一些特殊的算法。

    ArrayDeque的类描述

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

    其主要实现了Deque接口,下面看一看Deque要求提供的功能。

        void addFirst(E e);
        void addLast(E e);
        boolean offerFirst(E e);
        boolean offerLast(E e);
        E removeFirst();
        E removeLast();
        E pollFirst();
        E pollLast();
        E getFirst();
        E getLast();
        E peekFirst();
        E peekLast();
        // 删除在deque中第一次出现的指定元素
        boolean removeFirstOccurrence(Object o);
       // 删除在deque中最后一次出现的指定元素
        boolean removeLastOccurrence(Object o);
        // 以下API是为了与queue兼容,添加元素在尾节点操作,删除元素在头节点删除
        boolean add(E e);
        boolean offer(E e);
        E remove();
        E poll();
        E element();
        E peek();
        // 以下API是为了实现栈的语义
        void push(E e);
        E pop();
    

    ArrayDeque的成员变量

        // 用来存放元素的数组
        transient Object[] elements;
        // 头节点指针,指向当前元素,准确的说是指向remove将要删除的元素
        transient int head;
        // 尾节点指针,指向当前元素的下一个元素,准确的说是指向add将要添加的位置
        transient int tail;
        private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    

    支撑ArrayDeque的内部结构

    主要就是串并行迭代器,这两部分的代码很简单,就不贴出来了。

    ArrayDeque重要的函数

    添加元素相关函数

    // 插入头节点
    public void addFirst(E e) {
            if (e == null)
                throw new NullPointerException();
            final Object[] es = elements;
            // 因为head指向当前元素,所以需要先将索引向下挪动一位,再赋值,其中head向下挪动一位
            // 的方式是当前值减一,根据循环队列的规则,当减一越界的时候需要处理为数组尾部
            es[head = dec(head, es.length)] = e;
            // 当首位指针相同的时候,满了,扩容。ArrayDeque没有浪费一个容量来区分head==tail是是满还是空
            // 这也没有必要,再插入元素的相关函数中head==tail一定意味这元素满,需要扩容。
            // 在元素删除的相关函数中,head==tail一定意味着元素空,需要抛出异常。
            if (head == tail)
                grow(1);
        }
    static final int dec(int i, int modulus) {
            if (--i < 0) i = modulus - 1;
            return i;
        }
    private void grow(int needed) {
            // overflow-conscious code
            final int oldCapacity = elements.length;
            int newCapacity;
            // Double capacity if small; else grow by 50%
            int jump = (oldCapacity < 64) ? (oldCapacity + 2) : (oldCapacity >> 1);
            // 如果扩充的容量小于需求的容量或者扩充后超过了最大值,则处理一下
            if (jump < needed
                || (newCapacity = (oldCapacity + jump)) - MAX_ARRAY_SIZE > 0)
                newCapacity = newCapacity(needed, jump);
            // 扩容并且将元素复制过来,复制后的元素从数组0号位开始
            final Object[] es = elements = Arrays.copyOf(elements, newCapacity);
            // Exceptionally, here tail == head needs to be disambiguated
            // 重置tail和head
            if (tail < head || (tail == head && es[head] != null)) {
                // wrap around; slide first leg forward to end of array
                int newSpace = newCapacity - oldCapacity;
                // 将元素移到数组后部,从head + newSpace到最后
                System.arraycopy(es, head,
                                 es, head + newSpace,
                                 oldCapacity - head);
                // 将前面元素设置为null,将head位置调整为head + newSpace
                for (int i = head, to = (head += newSpace); i < to; i++)
                    es[i] = null;
            }
        }
    // 在尾端插入元素
     public void addLast(E e) {
            if (e == null)
                throw new NullPointerException();
            final Object[] es = elements;
            // 先设置值,tail指向下一次add的位置,所以应该先设置值
            es[tail] = e;
            // 然后调整tail位置,如果head和tail相等,则扩容
            if (head == (tail = inc(tail, es.length)))
                grow(1);
        }
    

    删除相关函数

    public E removeFirst() {
            E e = pollFirst();
            if (e == null)
                throw new NoSuchElementException();
            return e;
        }
    public E pollFirst() {
            final Object[] es;
            final int h;
            // 获取head指向元素
            E e = elementAt(es = elements, h = head);
            if (e != null) {
                es[h] = null;
                // head向上挪一位
                head = inc(h, es.length);
            }
            return e;
        }
    public E removeLast() {
            E e = pollLast();
            if (e == null)
                throw new NoSuchElementException();
            return e;
        }
    public E pollLast() {
            final Object[] es;
            final int t;
            // 因为tail是指向下一位,所以要先挪位再获取
            E e = elementAt(es = elements, t = dec(tail, es.length));
            if (e != null)
                es[tail = t] = null;
            return e;
        }
    

    get相关函数

    get相关函数十分简单,就不贴代码了。

    相关文章

      网友评论

          本文标题:Java集合之ArrayDeque

          本文链接:https://www.haomeiwen.com/subject/rqnwihtx.html