一文看懂ArrayDeque

1. 前言

      我们在实际的软件开发中经常会使用两种数据结构:栈和队列。Java语言有实现一个Stack的类,但Stack是一个古老的类,性能较差。Java提供了Queue接口,但没有提供Queue实现类。当我们需要用到Stack或Queue时,这是就推荐使用ArrayDeque。ArrayDeque是一个双端循环队列,既可以当成Stack使用,也可以当成Queue使用。

2. ArrayDeque的类继承图

在这里插入图片描述

3.ArrayDeque类法

3.1 构造函数

在这里插入图片描述

3.2常用方法

在这里插入图片描述

4. ArrayDeque 实现栈

栈的特点是后进先出,我们将push()方法,pop()方法进行组合使用,即可实现将ArrayDeque双端对列作为栈使用。

  private static void arrayDequeStackTest() {
        System.out.println("ArrayDeque:栈使用测试");
        ArrayDeque stack = new ArrayDeque();
        stack.push("宋江");                           //入栈
        stack.push("吴用");
        stack.push("公孙胜");
        System.out.println("栈中的元素");
        stack.forEach(o -> System.out.println(o));     //打印出栈中元素

        //出栈测试
        stack.forEach(o-> System.out.println(stack.pop()));

    }

运行结果:

ArrayDeque:栈使用测试
栈中的元素
公孙胜
吴用
宋江
出栈(后进先出):
公孙胜
吴用
宋江

5. ArrayDeque实现队列

我们知道队列的特点是先进先出,我们将offer()方法,pop()方法进行组合使用,即可将ArrayDeque双端对列作为队列使用。

//双向队列当队列使用
    private static void arrayDequeQueueTest( ){
        System.out.println("ArrayDeque:队列测试");
        ArrayDeque queue = new ArrayDeque();
        queue.offer("宋江");                         //入队
        queue.offer("吴用");
        queue.offer("公孙胜");
        System.out.println("队列中的元素");
        queue.forEach(o -> System.out.println(o));     //先进先出,先输出宋江

        System.out.println("出队(先进先出");
        queue.forEach(o -> System.out.println(queue.pop()));
    }

执行结果:

ArrayDeque:队列测试
队列中的元素
宋江
吴用
公孙胜
出队(先进先出
宋江
吴用
公孙胜

6. ArrayDeque底层算法研究

6.1 初始容量分配

6.1.2 初始容量分配算法

前面我们调到ArrayDeque双端队列容量分配都是2^n, 其容量分配算法如下:
在这里插入图片描述

6.1. 2 ^n 初始容量分配算法

我们先看下ArrayDeque实现 2^n容量分配的代码:

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

这个容量计算实现就很巧妙,其实现巧妙利用了位操作操作,具体如下:

  1. 它使用整数的最高位不断的进行无符号右移运算,并将结果与原值进行或操作,
    最后得到一个从最高位–最低位全1的数值。
  2. 将全1值再加1,最高位进1,其余为变0,即变成了比指定值更大,且为2^n值。

下面我们画图演示下创建容量为512的ArrayDeque,其创建2^n容量的过程。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

我们创建指定的容量为512,实际分配的数组容量为1024。

6.1.2 自动扩容

当ArrayDeque的队列使用过程,当队列满时,其自动创建一个容量翻倍的数组进行扩容。

7 双端队列ArrayDeque存储内存图

ArrayDeque是一个双端队列,其用成员head,tail分别表示队列的头和尾。
Head指向队列首元素,tail指向下一个可用队列元素空间

ArrayDeque statck = new ArrayDeque(6);

其内存图如下:
在这里插入图片描述
队首添加一个元素:

 stack.addFirst("宋江");

在这里插入图片描述

队尾添加一个元素:

stack.addLast("吴用");

在这里插入图片描述

8. ArrayDeque索引

1.队列数添加元素,head -1

elements[head = (head - 1) & (elements.length - 1)] = e;

head初始值为0,减1后变成-1, 与0x0000 0111(7)相与后变成最大索引7.
2.队尾添加元素,tail加1,

 (tail = (tail + 1) & (elements.length - 1)

9.总结

  1. ArrayDeque实现了双端队列,内部存储是基于一个动态扩展的数组。
  2. 通过位操作,对Head和Tail高效处理,对队列的首,尾元素进行操作效率高。
  3. 根据内容进行查找或删除的效率低,效率为O(N)。
  4. 使用ArrayDeque时,初始创建湿申请合适的大小,避免其反复扩容造成效率低下。
  5. ArrayDeque做为栈,FIFO队列使用时,需顺序添加,或删除元素。

我的公众号

分享嵌入式开发,java开发原创文章。
在这里插入图片描述

作者鼓励

如果您觉得本文帮助,打赏鼓励下
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值