飞道的博客

详解ArrayDeque_一个Collection接口下的万金油容器

317人阅读  评论(0)

目录

 

一、概述

二、底层实现

三、具体使用

 


一、概述

ArrayDequeCollection接口下Deque接口的一个实现,使用了可变数组,所以没有容量上的限制。

同时,ArrayDeque线程不安全的,在没有外部同步的情况下,不能再多线程环境下使用。

ArrayDequeDeque的实现类,可以作为栈来使用,效率高于Stack

也可以作为队列来使用,效率高于LinkedList。需要注意的是,ArrayDeque不支持null

 ArrayDeque也可以使用Iterator来迭代,并且可以从前向后和从后向前遍历。

所以说它是万金油的原因是:

  • 可以当做双端队列使用
  • 可以当做普通队列使用
  • 可以当做栈使用

二、底层实现

ArrayDeque底层逻辑的实现主要借助于一个环形逻辑计算公式,其实底层结构就是一个数组,但是下标的巧妙控制让它在逻辑上成环,从而可以适应各种结构的实现。

下面就举个实例给大家解释一下:


  
  1. public class MyArrayDeque<E> {
  2. private E[] elements; //存储元素的数组
  3. private int head; //头指针
  4. private int tail; //尾指针
  5. private final int INIT_CAPACITY = 16; //初始容量
  6. //初始化方法
  7. public MyArrayDeque() {
  8. elements = (E[]) new Object[INIT_CAPACITY];
  9. head = 0;
  10. tail = 0;
  11. }
  12. //头部添加
  13. public boolean offerFirst(E e) {
  14. if (e == null) {
  15. throw new NullPointerException();
  16. }
  17. //取余操作,避免数组空间浪费,同时防止下标越界,使数组在逻辑上成环
  18. head = (head - 1) & (elements.length - 1);
  19. elements[head] = e;
  20. if (head == tail) {
  21. //扩容操作
  22. expand();
  23. }
  24. return true;
  25. }
  26. //扩容
  27. private void expand() {
  28. int oldLen = elements.length;
  29. //二倍扩容
  30. int newLen = oldLen << 1;
  31. if (newLen < 0) {
  32. throw new IllegalStateException( "deque too big");
  33. }
  34. Object[] a = new Object[newLen];
  35. //resize
  36. int gap = oldLen - head;
  37. //拷贝head到原数组末尾的所有元素到新数组
  38. System.arraycopy(elements, head, a, 0, gap);
  39. System.arraycopy(elements, 0, a, gap, tail);
  40. //维护head和tail的下标
  41. head = 0;
  42. tail = oldLen;
  43. //扩容后再返回给elements
  44. elements = (E[]) a;
  45. }
  46. }

其实下面这段代码相当于是head=(head-1)%elements.length,但是我们都知道位运算的效率高,所以采用了位运算来取余(大家可以带入具体数值去验证这两个式子是否相等,是相等的。)

head = (head - 1) & (elements.length - 1);

至于为什么要(head-1),因为咱这是头插方法,所以头插之后head往前移动一位,维护head节点的正确性,不然插着插着没头这个标志位了往哪儿插呢...

之后大家可能就不难理解为什么要取余了,比如head现在在0下标位置,我现在再头插,那head不维护成-1了?这时候就进行取余(按位&操作会去符号,因为(elements.length-1)是个正数,符号位是0,0&任何数都是0,符号位就是0,正数,这没什么问题),这也是逻辑成环的关键所在,所以我画个图解释一下:

至于扩容时机,那就是head==tail时了,注意是二倍扩容,并且扩容后一定要维护head和tail的值,不然一直head==tail会死循环无限扩容的,具体实现就是上面代码中的expand()方法。


三、具体使用

既然是具体使用,那直接上代码了,该说的写到注释里:


  
  1. import java.util.ArrayDeque;
  2. import java.util.Iterator;
  3. public class ArrayDequeTest {
  4. public static void main(String[] args) {
  5. /**
  6. * 当做双端队列使用
  7. */
  8. System.out.println( "当做双端队列使用:");
  9. ArrayDeque<Integer> arrayDeque = new ArrayDeque<>();
  10. //添加元素
  11. arrayDeque.addFirst( 1);
  12. arrayDeque.addLast( 2);
  13. System.out.println( "测试offer方法的返回值:");
  14. System.out.println(arrayDeque.offerFirst( 0));
  15. System.out.println(arrayDeque.offerLast( 3));
  16. arrayDeque.addLast( 4);
  17. arrayDeque.addLast( 5);
  18. arrayDeque.addLast( 6);
  19. arrayDeque.addLast( 7);
  20. System.out.println( "添加元素操作的测试:");
  21. //正向遍历
  22. Iterator iterator = arrayDeque.iterator();
  23. while (iterator.hasNext()) {
  24. Object next = iterator.next();
  25. System.out.println(next);
  26. }
  27. //反向遍历
  28. System.out.println( "反向遍历:");
  29. Iterator iteratorReversed = arrayDeque.descendingIterator();
  30. while (iteratorReversed.hasNext()) {
  31. Object next = iteratorReversed.next();
  32. System.out.println(next);
  33. }
  34. //删除元素
  35. arrayDeque.removeFirst();
  36. arrayDeque.pollFirst();
  37. arrayDeque.removeLast();
  38. arrayDeque.pollLast();
  39. arrayDeque.removeFirstOccurrence( 4);
  40. arrayDeque.removeLastOccurrence( 5);
  41. System.out.println( "删除元素操作的测试:");
  42. Iterator iterator1 = arrayDeque.iterator();
  43. while (iterator1.hasNext()) {
  44. Object next = iterator1.next();
  45. System.out.println(next);
  46. }
  47. //获取元素
  48. System.out.println( "获取元素测试:");
  49. System.out.println( "获取第一个元素:" + arrayDeque.getFirst());
  50. System.out.println( "获取最后一个元素:" + arrayDeque.getLast());
  51. System.out.println( "元素个数:" + arrayDeque.size());
  52. System.out.println( "队列是否为空:" + arrayDeque.isEmpty());
  53. System.out.println( "队列中是否存在'2'元素:" + arrayDeque.contains( 2));
  54. System.out.println( "------------------------------------");
  55. /**
  56. * 当做普通队列使用
  57. */
  58. System.out.println( "当做普通队列使用");
  59. ArrayDeque<Integer> arrayDequeNormal = new ArrayDeque<>();
  60. //增
  61. arrayDequeNormal.add( 1);
  62. arrayDequeNormal.offer( 2);
  63. arrayDequeNormal.offer( 3);
  64. arrayDequeNormal.offer( 4);
  65. System.out.println( "队列中的第一个元素为:" + arrayDequeNormal.element());
  66. System.out.println( "队列中的第一个元素为:" + arrayDequeNormal.peek());
  67. //删
  68. arrayDequeNormal.remove();
  69. arrayDequeNormal.poll();
  70. System.out.println( "删除后剩余的元素:");
  71. //遍历
  72. Iterator iterator2 = arrayDequeNormal.iterator();
  73. while (iterator2.hasNext()) {
  74. Object next = iterator2.next();
  75. System.out.println(next);
  76. }
  77. System.out.println( "------------------------------------");
  78. /**
  79. * 当做栈来使用
  80. */
  81. System.out.println( "当做栈使用");
  82. ArrayDeque<Integer> arrayDequeStack = new ArrayDeque<>();
  83. //入栈
  84. arrayDequeStack.push( 1);
  85. arrayDequeStack.push( 2);
  86. arrayDequeStack.push( 3);
  87. arrayDequeStack.push( 4);
  88. //出栈
  89. arrayDequeStack.pop();
  90. Iterator iterator3 = arrayDequeStack.iterator();
  91. //遍历
  92. while (iterator3.hasNext()) {
  93. Object next = iterator3.next();
  94. System.out.println(next);
  95. }
  96. System.out.println( "------------------------------------");
  97. }
  98. }

大家可以在自己的环境下跑一下感受一下,万金油不是白叫的hh ^ ^


转载:https://blog.csdn.net/weixin_43729854/article/details/106529708
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场