java源码-ArrayDeque

简介: 开篇 Deque 接口继承自 Queue接口,但 Deque 支持同时从两端添加或移除元素,因此又被成为双端队列。鉴于此,Deque 接口的实现可以被当作 FIFO队列使用,也可以当作LIFO队列(栈)来使用。

开篇

 Deque 接口继承自 Queue接口,但 Deque 支持同时从两端添加或移除元素,因此又被成为双端队列。鉴于此,Deque 接口的实现可以被当作 FIFO队列使用,也可以当作LIFO队列(栈)来使用。官方也是推荐使用 Deque 的实现来替代 Stack。

 ArrayDeque 是 Deque 接口的一种具体实现,是依赖于可变数组来实现的。ArrayDeque 没有容量限制,可根据需求自动进行扩容。ArrayDeque不支持值为 null 的元素。


ArrayDeque类图

img_28c9c56a423bb069b5b78c7c60005227.png
ArrayDeque类图


ArrayDeque的类变量和构造函数

 ArrayDeque的类变量当中数组elements用来保存队列元素,head指针指向第一个存储元素,tail指向最后一个元素的下一个位置。
 ArrayDeque的calculateSize的逻辑非常巧妙,用于计算大于numElements的最小的2*n次方的值。

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

    // ArrayDeque采用数组来保存元素,通过头尾指针实现循环数组
    transient Object[] elements; // non-private to simplify nested class access
    
    // 第一个元素和最后一个元素的位置
    transient int head;
    transient int tail;

    private static final int MIN_INITIAL_CAPACITY = 8;
    
    // 计算数组大小的方式,实现大于numElements的最小的2*n次方的数字
    private static int calculateSize(int numElements) {
        int initialCapacity = MIN_INITIAL_CAPACITY;

        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;
    }
    
    // 分配数组的大小,calculateSize计算大小
    private void allocateElements(int numElements) {
        elements = new Object[calculateSize(numElements)];
    }


ArrayDeque的add相关操作

 ArrayDeque的add操作支持head端插入和tail端插入,head端插入是先计算位置后插入元素,tail端的插入是先保存元素后计算位置,所以会造成head的指针指向第一个元素的位置,tail指向最后一个元素的下一个位置。
 ArrayDeque的扩容时机是在head和tail相等的时候,根据上面分析我们可以得出ArrayDeque的容量达到(旧容量-1)的时候进行扩容
 ArrayDeque的头部插入过程是回退head指针后添加元素。
 ArrayDeque的尾部插入过程是添加元素后前进tail指针。

    public boolean offerFirst(E e) {
        addFirst(e);
        return true;
    }

    public void addFirst(E e) {
        if (e == null)
            throw new NullPointerException();
        //head往后移动一个位置放置新插入的元素
        //head指向第一个元素的下标
        elements[head = (head - 1) & (elements.length - 1)] = e;
        if (head == tail)
            doubleCapacity();
    }




    public boolean offerLast(E e) {
        addLast(e);
        return true;
    }
    
    //tail往前移动一个位置放置新插入的元素
    //tail指向末尾元素的下一个位置,注意是末尾元素的下一个位置
    public void addLast(E e) {
        if (e == null)
            throw new NullPointerException();
        elements[tail] = e;
        //tail循环以后和head保持一个空余位置,也就是说head=tail+1的时候进行扩容
        if ( (tail = (tail + 1) & (elements.length - 1)) == head)
            doubleCapacity();
    }


ArrayDeque的remove相关操作

 ArrayDeque的remove操作包括从head开始删除和从tail开始删除。

  • 头部删除pollFirst()方法返回head指针指向的元素同时向后移动一个位置
  • 尾部删除pollLast()方法返回tail指针指向位置的前一个位置的元素后tail指针往前移动一个位置
    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")
        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;
    }

    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;
    }

    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)];
    }


ArrayDeque的扩容过程

 ArrayDeque的扩容过程如下:

  • 以2倍速率进行扩容(int newCapacity = n << 1)
  • 拷贝下标head到数组末尾的元素到新数组
  • 拷贝下标0到tail指针的元素到新数组
    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;
    }


ArrayDeque的操作图解过程

img_0919489efa57543388c8348483d4bb2a.png
ArrayDeque添加过程

img_5a25b8af1c64aee11e8a7acf42dde71e.png
ArrayDeque扩容过程


参考文章

Java 容器源码分析之 Deque 与 ArrayDeque

目录
相关文章
|
7天前
|
运维 Java
Java版HIS系统 云HIS系统 云HIS源码 结构简洁、代码规范易阅读
云HIS系统分为两个大的系统,一个是基层卫生健康云综合管理系统,另一个是基层卫生健康云业务系统。基层卫生健康云综合管理系统由运营商、开发商和监管机构使用,用来进行运营管理、运维管理和综合监管。基层卫生健康云业务系统由基层医院使用,用来支撑医院各类业务运转。
30 5
|
2天前
|
搜索推荐 前端开发 Java
java医院绩效考核管理系统项目源码
系统需要和his系统进行对接,按照设定周期,从his系统获取医院科室和医生、护士、其他人员工作量,对没有录入信息化系统的工作量,绩效考核系统设有手工录入功能(可以批量导入),对获取的数据系统按照设定的公式进行汇算,且设置审核机制,可以退回修正,系统功能强大,完全模拟医院实际绩效核算过程,且每步核算都可以进行调整和参数设置,能适应医院多种绩效核算方式。
3 0
|
2天前
|
设计模式 算法 Java
[设计模式Java实现附plantuml源码~行为型]定义算法的框架——模板方法模式
[设计模式Java实现附plantuml源码~行为型]定义算法的框架——模板方法模式
|
2天前
|
设计模式 JavaScript Java
[设计模式Java实现附plantuml源码~行为型] 对象状态及其转换——状态模式
[设计模式Java实现附plantuml源码~行为型] 对象状态及其转换——状态模式
|
2天前
|
设计模式 存储 JavaScript
[设计模式Java实现附plantuml源码~创建型] 多态工厂的实现——工厂方法模式
[设计模式Java实现附plantuml源码~创建型] 多态工厂的实现——工厂方法模式
|
2天前
|
设计模式 Java Go
[设计模式Java实现附plantuml源码~创建型] 集中式工厂的实现~简单工厂模式
[设计模式Java实现附plantuml源码~创建型] 集中式工厂的实现~简单工厂模式
|
3天前
|
Java 调度
Java面试必考题之线程的生命周期,结合源码,透彻讲解!
Java面试必考题之线程的生命周期,结合源码,透彻讲解!
29 1
|
7天前
|
JavaScript Java 测试技术
基于Java的电影评论系统的设计与实现(源码+lw+部署文档+讲解等)
基于Java的电影评论系统的设计与实现(源码+lw+部署文档+讲解等)
24 0
|
7天前
|
JavaScript Java 测试技术
基于Java的在线日语培训平台的设计与实现(源码+lw+部署文档+讲解等)
基于Java的在线日语培训平台的设计与实现(源码+lw+部署文档+讲解等)
23 0
|
7天前
|
JavaScript Java 测试技术
基于Java的同城蔬菜配送管理系统的设计与实现(源码+lw+部署文档+讲解等)
基于Java的同城蔬菜配送管理系统的设计与实现(源码+lw+部署文档+讲解等)
11 0