当前位置:   article > 正文

java数据结构之双端队列ArrayDeque_java双端队列

java双端队列

这篇文章介绍java的数据结构之双端队列之ArrayDeque

1、ArrayDeque

ArrayDeque是一个双端数组,既可以当做栈使用,也可以当做队列使用。

ArrayDeque有两个指针head和tail分别指向数据开头和数据结尾,当你从一端进行数据的插入和弹出时,它可以当做栈使用;当你从一端进行数据的插入,从另一端进行数据的弹出时,它可以当做队列使用;当然也可以从两端同时进行数据的插入和弹出,这种情况就比较灵活了,具体在使用在什么场景下,目前还不太清楚,带我仔细研究后再加说明。

本篇文章从源代码的角度讨论ArrayDeque的数据结构,以及数据的插入和弹出操作。

2、ArrayDeque的初始化

(1)、当你不指定ArrayDeque数组的长度时,默认长度是16

(2)、当你指定ArrayDeque数组的长度时,代码会将数组长度转化为2的整次幂。比如如果你传的长度是18,则会转化为比18大但是2的整次幂的数32,也就是实际数组的长度为32。

注:数组长度不能小于8,如果指定的数组长度小于8,则按8进行处理。

构造函数如下:

  1. //默认长度16
  2. public ArrayDeque() {
  3. elements = new Object[16];
  4. }
  5. //如果指定长度,会调用allocateElements进行长度的归一化
  6. public ArrayDeque(int numElements) {
  7. allocateElements(numElements);
  8. }
  9. private void allocateElements(int numElements) {
  10. elements = new Object[calculateSize(numElements)];
  11. }
  12. private static int calculateSize(int numElements) {
  13. int initialCapacity = MIN_INITIAL_CAPACITY;
  14. // Find the best power of two to hold elements.
  15. // Tests "<=" because arrays aren't kept full.
  16. if (numElements >= initialCapacity) {
  17. initialCapacity = numElements;
  18. initialCapacity |= (initialCapacity >>> 1);
  19. initialCapacity |= (initialCapacity >>> 2);
  20. initialCapacity |= (initialCapacity >>> 4);
  21. initialCapacity |= (initialCapacity >>> 8);
  22. initialCapacity |= (initialCapacity >>> 16);
  23. initialCapacity++;
  24. if (initialCapacity < 0) // Too many elements, must back off
  25. initialCapacity >>>= 1;// Good luck allocating 2 ^ 30 elements
  26. }
  27. return initialCapacity;
  28. }

初始化之后,ArrayDeque的初始结构如下:

初始化结构展示

3、数据的插入

(1)、插入数据有两个方法,addFirst(E e) 和 addLast(E e),其中

        addFirst(E e):操作head指针,从头部插入(逻辑上)

        addLast(E e):操作tail指针,从尾部插入(逻辑上)

3.1 addFirst(0):

 addFirst(E e)源码解释:

  1. public void addFirst(E e) {
  2. if (e == null)
  3. throw new NullPointerException();
  4. elements[head = (head - 1) & (elements.length - 1)] = e;
  5. if (head == tail)
  6. doubleCapacity();
  7. }

说明1:代码中(head - 1) & (elements.length - 1)的作用:

进行(head - 1) & (elements.length - 1)计算之后才赋予head的原因是防止head指针的下标小于0

为了防止这种情况发生(假如数组长度elements.length = 16):

(1)、当head = 0时,head - 1 = -1,数组的下标是不能小于0的,为了防止这种情况发生,将 -1 与 15进行与运算,得到的结果是15。也就是说当head下标小于0时,将head指向到数组的最大处。

3.2 addLast(1)

 addLast(E e)源码解释:

  1. public void addLast(E e) {
  2. if (e == null)
  3. throw new NullPointerException();
  4. elements[tail] = e;
  5. if ( (tail = (tail + 1) & (elements.length - 1)) == head)
  6. doubleCapacity();
  7. }

说明2:代码中(tail + 1) & (elements.length - 1)的作用:

进行(tail + 1) & (elements.length - 1)计算之后才赋予tail的原因是防止tail指针的下标大于数组的最大下标

为了防止这种情况发生(假如数组长度elements.length = 16):

(1)、当tail= 15时,tail + 1 = 16,数组的下标是不能大于16的,为了防止这种情况发生,将 16 与 15进行与运算,得到的结果是0。也就是说当tail下标大于数组的最大下标时,将tail指向到数组下标为0处。

4、获取数据

 为了更好的说明,我们在head指针插入偶数,tail指针插入奇数,假如我们的数据结构现在如下:

获取数据有4个方法:

peekFirst():

操作head指针,获取head指针处的数据,但不移动head指针(只获取数据,不删除数据)

操作之后数据结构如下:

pollFirst(): 

操作head指针,获取head指针处的数据,移动head指针(获取数据,并从数组中删除数据)

操作之后数据结构如下:

peekLast():

操作tail指针,获取tail指针处的数据,但不移动tail指针(只获取数据,不删除数据)

操作之后数据结构如下:

pollLast():

操作tail指针,获取tail指针处的数据,移动tail指针(获取数据,并从数组中删除数据)

操作之后数据结构如下:

 5、数组扩展

 当head = tail时,表示此时数组已经塞满,需要进行扩展,扩展代码在doubleCapacity()方法体中,代码如下:

  1. private void doubleCapacity() {
  2. assert head == tail;
  3. int p = head;
  4. int n = elements.length;
  5. int r = n - p; // number of elements to the right of p
  6. int newCapacity = n << 1; //作者注:数组长度扩展一倍
  7. if (newCapacity < 0)
  8. throw new IllegalStateException("Sorry, deque too big");
  9. Object[] a = new Object[newCapacity];
  10. System.arraycopy(elements, p, a, 0, r); //作者注:将原数组head到末尾的数据拷贝到新数组从0下标下标位置
  11. System.arraycopy(elements, 0, a, r, p); //作者注:将原数组0到tail的数据拷贝到新数组。
  12. elements = a;
  13. head = 0;
  14. tail = n;
  15. }

下面通过直观的数据结构表现这部分代码是如何扩展数组的。

假设原来的数组长度是16,已经塞满,如下:

 扩展之后的数组长度是32,数据拷贝之后形成的结构如下:

 6、ArrayDeque使用场景

1、作者是在追踪Flink的barrier的处理过程中,看到了ArrayDeque的使用,在Flink中,barrier的添加和删除相当于是使用了ArrayDeque的队列功能,从tail添加,从head删除。

2、ArrayDeque 作为队列使用时性能比 LinkedList 好,作为栈使用时性能比Stack好。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/78786
推荐阅读
相关标签
  

闽ICP备14008679号