ArrayDeque原码分析

主要特点

  1. ArrayDeque是通过可变数组的方式实现了Deque接口。
  2. ArrayDeque没有容量限制,为了满足需求会自动容量会自动增长。
  3. ArrayDeque不是线程安全的。
  4. 不允许存放null。
  5. 当作stack使用时,效率比java.util.Stack快;当作queue使用时,效率比java.util.LinkedList快。
  6. 大部分的操作都是常数时间运行

JDK实现的关键点

将数组当作环来使用

   

    在ArrayDeque中,将数组当成环形来使用的。head 指向首端第一个有效元素, tail 指向尾端第一个可以插入元素的空位,因为是循环数组,所以 head 不一定总等于0, tail 也不一定总是比head 大。

   通过掩码运算避免数组越界

    通过calculateSize方法确保,数组的长度为2的倍数,这样就可以通过掩码运算来避免判断数组越界。

如何当前是数组的大小16,那么最大的索引值就是(length - 1)就是15, 如果此时(tail + 1) 正好超出索引值也就是最后位置增1 = 16,但是通过掩码运算(tail = (1000 & 0111))得到0

如果当前是数组的大小16,那么最大的索引值就是(length - 1)就是15,如果此时(head - 1) 正好超出索引值也就是第一位减1 = -1,但是通过掩码运算(tail = (1111 & 0111))会得到15

当head跟tail相等时,对数组进行扩容

    // ******  Array allocation and resizing utilities ******

    private static int calculateSize(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
        }
        return initialCapacity;
    }

数组扩容操作

如果tail值与head相等则翻倍扩展,在扩容时需要进行数组拷贝

 /**
     * 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;
        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 = a;
        head = 0;
        tail = n;
    }

在尾部添加元素

add(e) addLast(e) offer(e) offerLast(e),这几个方法最终都调用了addLast(e)

先将elements[tail]设置为需要插入的元素,插入之后将tail+1,并判断是否可以扩容数组,这里其实牺牲了一个数组的位置。

    /**
     * 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();
        elements[tail] = e;
        if ( (tail = (tail + 1) & (elements.length - 1)) == head)
            doubleCapacity();
    }

在头部添加元素

    offerFirst(E e) addFirst(E e)

    /**
     * Inserts the specified element at the front of this deque.
     *
     * @param e the element to add
     * @throws NullPointerException if the specified element is null
     */
    public void addFirst(E e) {
        if (e == null)
            throw new NullPointerException();
        elements[head = (head - 1) & (elements.length - 1)] = e;
        if (head == tail)
            doubleCapacity();
    }

从头部获取(删除)元素

    方法element()、peek()、peekFirst()、getFirst()不需要从数组中删除元素,只需要根据head从数组中获取(elements[head])即可。

    方法poll()、pollFirst()、pop()、removeFirst()获取元素之后,要将元素删除,需要将数组索引head中值设置为null,以便虚拟机的垃圾回收器回收,并重新设置head的值。这几个方法最终都是调用了pollFirst(),代码示例如下:

   public E pollFirst() {
        int h = head;
        @SuppressWarnings("unchecked")
        E result = (E) elements[h];
        // Element is null if deque empty
        if (result == null)
            return null;
        elements[h] = null;     // Must null out slot
        head = (h + 1) & (elements.length - 1);
        return result;
    }

从尾部获取(删除)元素

    方法getLast()、peekLast()获取尾部元素,因为tail值是数组中最后一位的索引加一之后的结果,所以在获取时要减去一


    /**
     * @throws NoSuchElementException {@inheritDoc}
     */
    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 peekLast() {
        return (E) elements[(tail - 1) & (elements.length - 1)];
    }

    方法pollLast()以及removeLast()获取并删除尾部元素,所以在获取到元素之后,需要将数组中对应索引位置的元素设置为null,以便虚拟机垃圾回收器进行回收。removeLast()方法里面调用了pollLast(),所以这里只展示pollLast()的代码:

    public E pollLast() {
        int t = (tail - 1) & (elements.length - 1);
        @SuppressWarnings("unchecked")
        E result = (E) elements[t];
        if (result == null)
            return null;
        elements[t] = null;
        tail = t;
        return result;
    }

是否包含元素

该方法从head开始,利用掩码运算,遍历整个数组,判断某个元素是否在数组中

    /**
     * Returns {@code true} if this deque contains the specified element.
     * More formally, returns {@code true} if and only if this deque contains
     * at least one element {@code e} such that {@code o.equals(e)}.
     *
     * @param o object to be checked for containment in this deque
     * @return {@code true} if this deque contains the specified element
     */
    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;
    }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值