ArrayDeque源码阅读

ArrayDeque 动态循环数组的指针和扩容代码

ArrayDeque是jdk1.6中添加的一个集合处理类。继承AbstractCollection,实现Deque双端队列接口。
主要用于实现栈和队列。

ArrayDeque底层是一个动态循环数组。	
主要的实例变量为head,tail,和元素的保存位置,一个Object类型的数组。
初始化的时候 head = tail = 0 这个时候,首位指针都指向数组中的0位。通过移动head/tail的位置来处理元素的位置。
ArrayDeque默认长度为16(当使用其他集合初始化的时候,底层元素长度为传入的集合的长度+1)
底层数组没有元素的位置均为空且数组尾部最少有一个空

位置处理方法:inc方法用于将head/tail向后移动,dec方法用于将head/tail向前移动。后面的三参数inc和sub是这两个方法的变形,将移动的距离从固定的1变成参数传递。

	static final int inc(int i, int modulus) {
        if (++i >= modulus) i = 0;
        return i;
    }
    static final int dec(int i, int modulus) {
        if (--i < 0) i = modulus - 1;
        return i;
    }
    static final int inc(int i, int distance, int modulus) {
        if ((i += distance) - modulus >= 0) i -= modulus;
        return i;
    }
    static final int sub(int i, int j, int modulus) {
        if ((i -= j) < 0) i += modulus;
        return i;
    }

ArrayDeque中处理元素的方法主要通过移动头尾指针来处理。在增加元素的时候,分别移动头/尾方法对应的指针,头指针前一,尾指针后移,并将添加的元素放置在移动后指针所在的位置上。在移除元素的时候,头指针后移,尾指针前移,并将原来位置上的元素设置为null;添加的元素是不能为空的,或者会触发NullPointerException。

用作栈和队列的时候,栈处理的方法就是从头部添加,头部拿出,队列的处理方法就是头部插入,尾部拿出。
	//从头部添加
	public void addFirst(E e) {
        if (e == null)
            throw new NullPointerException();
        final Object[] es = elements;
        es[head = dec(head, es.length)] = e;
        if (head == tail)
            grow(1);
    }
    //从尾部添加
    public void addLast(E e) {
        if (e == null)
            throw new NullPointerException();
        final Object[] es = elements;
        es[tail] = e;
        if (head == (tail = inc(tail, es.length)))
            grow(1);
    }
    //从头部移除
    public E pollFirst() {
        final Object[] es;
        final int h;
        E e = elementAt(es = elements, h = head);
        if (e != null) {
            es[h] = null;
            head = inc(h, es.length);
        }
        return e;
    }
    //从尾部移除
    public E pollLast() {
        final Object[] es;
        final int t;
        E e = elementAt(es = elements, t = dec(tail, es.length));
        if (e != null)
            es[tail = t] = null;
        return e;
    }

扩容方法:触发时机,在放入元素之后头尾指针相遇,或者addAll方法之后现有的数组中的空余位置不足以容纳新增的元素。扩容新增的容量结果分别为原来的容量+2(原来的容量<64)或者原来的容量*1.5(原有容量大于等于64) 如果这样比需要新增的容量小,则直接新增传入的容量。容量最大为Integer.MAX_VALUE-8;

    private void grow(int needed) {
        //旧容量
        final int oldCapacity = elements.length;
        //新容量
        int newCapacity;
        //如果原有体积较小则容量翻倍 或者就增加50%   比较的标准是旧容量是否小于64 这里的jump代表增加的容量
        int jump = (oldCapacity < 64) ? (oldCapacity + 2) : (oldCapacity >> 1);
        //如果上一次计算得到的容量低于所需的容量 则对增加的容量再次进行计算
        if (jump < needed
            || (newCapacity = (oldCapacity + jump)) - MAX_ARRAY_SIZE > 0){
                //对新的容量进行溢出计算
                newCapacity = newCapacity(needed, jump);
            }
        //数组扩容
        final Object[] es = elements = Arrays.copyOf(elements, newCapacity);
        if (tail < head || (tail == head && es[head] != null)) {
            // 这里代表头尾指针相遇 这个时候需要将头指针向后移动新增加的容量长度
            int newSpace = newCapacity - oldCapacity;
            //复制旧的数组给新的数组
            System.arraycopy(es, head,es, head + newSpace,oldCapacity - head);
            //将新增容量的地方全部赋值null
            for (int i = head, to = (head += newSpace); i < to; i++)
                es[i] = null;
        }
    }

LinkedList和ArrayDeque的区别

LinkedList底层是双向链表结构,ArrayDeque底层是动态循环数组
做队列的时候ArrayDeque效率快于LinkedList
ArrayDeque的操作(插入、删除、访问)效率为均摊的O(1)
LinkedList的操作(插入、删除)效率为O(1) 访问为O(n) 这是链表实现造成的效率
ArrayDeque不可操作null,LinkedList可以操作空
LinkedList因为需要在元素本身之外存储双向链表的指针 所以大部分情况下占用的内容都要大于ArrayDeque
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值