1、Deque-接口包含方法-简介

  1. public interface Deque<E> extends Queue<E> {
  2. // 添加元素到队列头
  3. void addFirst(E e);
  4. // 添加元素到队列尾
  5. void addLast(E e);
  6. // 添加元素到队列头
  7. boolean offerFirst(E e);
  8. // 添加元素到队列尾
  9. boolean offerLast(E e);
  10. // 从队列头移除元素
  11. E removeFirst();
  12. // 从队列尾移除元素
  13. E removeLast();
  14. // 从队列头移除元素
  15. E pollFirst();
  16. // 从队列尾移除元素
  17. E pollLast();
  18. // 查看队列头元素
  19. E getFirst();
  20. // 查看队列尾元素
  21. E getLast();
  22. // 查看队列头元素
  23. E peekFirst();
  24. // 查看队列尾元素
  25. E peekLast();
  26. // 从队列头向后遍历移除指定元素
  27. boolean removeFirstOccurrence(Object o);
  28. // 从队列尾向前遍历移除指定元素
  29. boolean removeLastOccurrence(Object o);
  30. // *** 队列中的方法 ***
  31. // 添加元素,等于addLast(e)
  32. boolean add(E e);
  33. // 添加元素,等于offerLast(e)
  34. boolean offer(E e);
  35. // 移除元素,等于removeFirst()
  36. E remove();
  37. // 移除元素,等于pollFirst()
  38. E poll();
  39. // 查看元素,等于getFirst()
  40. E element();
  41. // 查看元素,等于peekFirst()
  42. E peek();
  43. // *** 栈方法 ***
  44. // 入栈,等于addFirst(e)
  45. void push(E e);
  46. // 出栈,等于removeFirst()
  47. E pop();
  48. // *** Collection中的方法 ***
  49. // 删除指定元素,等于removeFirstOccurrence(o)
  50. boolean remove(Object o);
  51. // 检查是否包含某个元素
  52. boolean contains(Object o);
  53. // 元素个数
  54. public int size();
  55. // 迭代器
  56. Iterator<E> iterator();
  57. // 反向迭代器
  58. Iterator<E> descendingIterator();
  59. }

2、ArrayDeque-底层数据结构

  1. 1ArrayDeque底层使用了数组来存储数据,同时用两个intheadtail来表示头部和尾部。
  2. //用数组存储元素
  3. transient Object[] elements; // non-private to simplify nested class access
  4. //头部元素的索引
  5. transient int head;
  6. //尾部元素的下一位,即下一个将要被加入的元素的索引[!!!]
  7. transient int tail;
  8. //最小容量,必须为2的幂次方
  9. private static final int MIN_INITIAL_CAPACITY = 8;

3、ArrayDeque-构造方法

  1. 1ArrayDeque有三个构造函数来初始化,除了无参的构造函数使用了默认容量,其它两个构造函数会通过allocateElements来计算初始容量。
  2. 2、源码:
  3. public ArrayDeque() {
  4. elements = (E[]) new Object[16]; // 默认的数组长度大小
  5. }
  6. public ArrayDeque(int numElements) {
  7. allocateElements(numElements); // 需要的数组长度大小,指定元素个数初始化
  8. }
  9. public ArrayDeque(Collection<? extends E> c) {
  10. allocateElements(c.size()); // 根据集合来分配数组大小
  11. addAll(c); // 把集合中元素放到数组中
  12. }

4、ArrayDeque-分配数组大小

  1. 1ArrayDeque 对数组的大小(即队列的容量)有特殊的要求,必须是 2^n[!!!]。
  2. 2、源码:
  3. //初始化数组,计算出大于var1的最接近的2的n次方且大于等于8
  4. //比如:3-->8、9-->16
  5. //对于一个小于2^30的值,经过五次右移和位或操作后,可以得到一个2^k - 1的值。最后再将这个值+1,得到2^k。
  6. //如果传入的值大于等于2^30,那么经过转化会变成负值,即< 0,此时会把初始值设置为2^30,即最大的容量只有2^30。
  7. //无符号右移再进行按位或操作,就是将其低位全部补成1,然后再自加加一次,就是再向前进一位。
  8. //这样就能得到其最小的2次幂。之所以需要最多移16位,是为了能够处理大于2^16次方数。
  9. private void allocateElements(int var1) {
  10. int var2 = 8;
  11. if (var1 >= var2) {
  12. // 5次位移可以得到2的n次幂减1
  13. var2 = var1 | var1 >>> 1;
  14. var2 |= var2 >>> 2;
  15. var2 |= var2 >>> 4;
  16. var2 |= var2 >>> 8;
  17. var2 |= var2 >>> 16;
  18. ++var2;
  19. // 传2的n次幂会有问题,最终的值是2的n+1次幂
  20. if (var2 < 0) {
  21. var2 >>>= 1;
  22. }
  23. }
  24. this.elements = (Object[])(new Object[var2]);
  25. }

5、ArrayDeque-扩容

  1. 1、扩容:无论是从头部还是从尾部添加元素,都会判断tail==head,如果两个索引相遇,说明数组空间已满,需要扩容操作。
  2. 2、源码:
  3. private void doubleCapacity() {
  4. assert this.head == this.tail; //扩容时头部索引和尾部索引肯定相等
  5. int var1 = this.head; //头指针的位置
  6. int var2 = this.elements.length; //旧数组的长度
  7. int var3 = var2 - var1; //旧数组中,头指针离数组尾部的距离
  8. int var4 = var2 << 1; //左移一位,相当于*2,即把新数组长度变为原来的两倍
  9. //判断数组的长度是否小于0,也是判断是否有溢出
  10. if (var4 < 0) {
  11. throw new IllegalStateException("Sorry, deque too big");
  12. } else {
  13. Object[] var5 = new Object[var4]; //新建一个数组,长度为旧长度的两倍
  14. System.arraycopy(this.elements, var1, var5, 0, var3);//将旧数组head之后的元素拷贝到新数组中
  15. System.arraycopy(this.elements, 0, var5, var3, var1);//将旧数组下标0到head之间的元素拷贝到新数组中
  16. this.elements = (Object[])var5;// 赋值为新数组
  17. // head指向0,tail指向旧数组长度表示的位置
  18. this.head = 0;
  19. this.tail = var2;
  20. }
  21. }
  22. // 拷贝该数组的所有元素到目标数组
  23. private <T> T[] copyElements(T[] a) {
  24. if (head < tail) { // 开始索引大于结束索引,一次拷贝
  25. System.arraycopy(elements, head, a, 0, size());
  26. } else if (head > tail) { // 开始索引在结束索引的右边,分两段拷贝
  27. int headPortionLen = elements.length - head;
  28. System.arraycopy(elements, head, a, 0, headPortionLen);
  29. System.arraycopy(elements, 0, a, headPortionLen, tail);
  30. }
  31. return a;
  32. }

5-1、ArrayDeque-扩容-源码分析-示意图

容器(集合)-ArrayDeque-源码分析 - 图1

6、ArrayDeque-添加元素

  1. 1、添加元素,此处也是指入队,主要有两种方式:从队列头部、从队列尾部、
  2. 2、入队的方法,有很多,此处分析,addFirst(E e)和addLast(E e)
  3. 2-1、.addFirst(E e)源码:在head的前面插入元素,在空间足够且下标没有越界的情况下,只需要将elements[--head] = e
  4. //将元素从数组的最后一个位置向前依次存放。
  5. public void addFirst(E var1) {
  6. if (var1 == null) {
  7. throw new NullPointerException();
  8. } else {
  9. /**
  10. 1、将head指针减1并与数组长度减1取模,防止数组到头了边界溢出,如果到头了就从尾再向前。
  11. 2、elements.length一定是2的n次幂,转换为二进制就是n+1位为1,其他位为0。
  12. [比如:4 = 2^2(10) = 100(2)]
  13. 3、那么elements.length-1,从右到左n位都是1,比如4(10)=100(2)-1=011(2)。
  14. 4、第一次添加时,head=0,head-1=-1(10)=[00000001]原=[11111110]反
  15. 5、举例:elements.length=16,那么第一次添加:this.head = this.head - 1 & this.elements.length - 1 =
  16. 11111110
  17. & 01111000
  18. ------------
  19. 01111000 ==> 15
  20. 第二次添加时head=15 head - 1 = 14 转为二进制 1110,1110 & 1111结果为1110(14)
  21. 6、可推出:实际上head = (head - 1) & (elements.length - 1)就是最后一位开始递减
  22. */
  23. //从最后一位开始依次向前添加元素
  24. //x & (len - 1) = x % len,使用&的方式更快;
  25. //相当于取余,同时解决了head为负值的情况
  26. this.elements[this.head = this.head - 1 & this.elements.length - 1] = var1;
  27. // 当head==tail时进行扩容
  28. if (this.head == this.tail) {
  29. this.doubleCapacity();
  30. }
  31. }
  32. }
  33. 2-2、.addLast(E e)源码:在tail的位置插入元素,由于tail总是指向下一个可以插入的空位,因此只需要elements[tail] = e;
  34. //将元素从数组的第一个位置依次向后存放。
  35. public void addLast(E var1) {
  36. if (var1 == null) {
  37. throw new NullPointerException();
  38. } else {
  39. //tail指针指向的是队列最后一个元素的下一个位置,在尾指针的位置放入元素。
  40. //是把元素放在tail位置之后再把tail+1的,也就是说tail下标位置是没有值的,也就是含头不含尾。
  41. this.elements[this.tail] = var1;
  42. //为了处理临界情况 (tail为length - 1时),和length - 1进行与操作,结果为0。
  43. //比如:elements.length = 16,当head为0,tail当前值为15时实际上数组这时已经满了,那么tail + 1 = 16, elements.length - 1 = 15, head = 0,那么10000 & 1111 = 00000000 == head
  44. //x & (len - 1) = x % len,使用&的方式更快;
  45. if ((this.tail = this.tail + 1 & this.elements.length - 1) == this.head) {
  46. this.doubleCapacity();
  47. }
  48. }
  49. }
  50. 2-3
  51. public boolean offerFirst(E e) {
  52. addFirst(e);
  53. return true;
  54. }
  55. 2-4
  56. public boolean offerLast(E e) {
  57. addLast(e);
  58. return true;
  59. }

6-1、ArrayDeque-头部添加元素-示意图


容器(集合)-ArrayDeque-源码分析 - 图2

6-2、ArrayDeque-尾部添加元素-示意图

容器(集合)-ArrayDeque-源码分析 - 图3
容器(集合)-ArrayDeque-源码分析 - 图4

7、ArrayDeque-取出元素

  1. 1、取出元素,方法有很多,此处只介绍.pollFirst()和.pollLast()。
  2. 2、源码分析:
  3. //删除并返回Deque首端元素,也即是head位置处的元素。
  4. public E pollFirst() {
  5. int var1 = this.head;
  6. //取出队列头元素
  7. Object var2 = this.elements[var1];
  8. if (var2 == null) {
  9. return null;
  10. } else {
  11. this.elements[var1] = null;//将队列头置为空
  12. // 队列头指针右移一位,即头指针加1再和数组最大下标按位与计算出新的head
  13. this.head = var1 + 1 & this.elements.length - 1;
  14. return var2;
  15. }
  16. }
  17. //删除并返回Deque尾端元素,也即是tail位置前面的那个元素。
  18. //注意:出队之后没有缩容
  19. //每次pollLast()前tail先减1,然后再删除,tail指向的位置在元素的上一个位置。
  20. //pollLast() 也是绕着数组循环删除的。tail一直绕着数组循环转动。
  21. public E pollLast() {
  22. //尾指针左移一位, 取模的方式让头尾指针在数组范围内循环
  23. int var1 = this.tail - 1 & this.elements.length - 1;
  24. //取当前尾指针处元素
  25. Object var2 = this.elements[var1];
  26. if (var2 == null) {
  27. return null;
  28. } else {
  29. // 将当前尾指针处置为空
  30. this.elements[var1] = null;
  31. // tail指向新的尾指针处,将取出到的元素位置索引赋给tail,tail处值为null,含头不含尾
  32. this.tail = var1;
  33. // 返回取得的元素
  34. return var2;
  35. }
  36. }
  37. //返回但不删除Deque首端元素,也即是head位置处的元素,直接返回elements[head]即可。
  38. public E peekFirst() {
  39. return this.elements[this.head];
  40. }
  41. //返回但不删除Deque尾端元素,也即是tail位置前面的那个元素。
  42. public E peekLast() {
  43. return this.elements[this.tail - 1 & this.elements.length - 1];
  44. }

8、ArrayDeque-删除元素

  1. 1、删除元素 删除首尾元素
  2. 2、源码:
  3. //removeFirst() 就是调用pollFirst() 不同之处在于在当pollFirst()返回null removeFirst() 抛出 null 元素异常。
  4. public E removeFirst() {
  5. Object var1 = this.pollFirst();
  6. if (var1 == null) {
  7. throw new NoSuchElementException();
  8. } else {
  9. return var1;
  10. }
  11. }
  12. //removeLast() 就是调用pollLast() 不同之处在于在当pollLast()返回null removeLast() 抛出 null 元素异常。
  13. public E removeLast() {
  14. Object var1 = this.pollLast();
  15. if (var1 == null) {
  16. throw new NoSuchElementException();
  17. } else {
  18. return var1;
  19. }
  20. }
  21. public boolean removeFirstOccurrence(Object var1) {
  22. if (var1 == null) {
  23. return false;
  24. } else {
  25. int var2 = this.elements.length - 1;
  26. Object var4;
  27. for(int var3 = this.head; (var4 = this.elements[var3]) != null; var3 = var3 + 1 & var2) {
  28. if (var1.equals(var4)) {
  29. this.delete(var3);
  30. return true;
  31. }
  32. }
  33. return false;
  34. }
  35. }
  36. public boolean removeLastOccurrence(Object var1) {
  37. if (var1 == null) {
  38. return false;
  39. } else {
  40. int var2 = this.elements.length - 1;
  41. Object var4;
  42. for(int var3 = this.tail - 1 & var2; (var4 = this.elements[var3]) != null; var3 = var3 - 1 & var2) {
  43. if (var1.equals(var4)) {
  44. this.delete(var3);
  45. return true;
  46. }
  47. }
  48. return false;
  49. }
  50. }

9、ArrayDeque-栈|队列-操作方法

  1. 1、栈-操作
  2. public void push(E e) {
  3. addFirst(e);
  4. }
  5. public E pop() {
  6. return removeFirst();
  7. }
  8. 2、队列操作
  9. public boolean add(E e) {
  10. addLast(e);
  11. return true;
  12. }
  13. public boolean offer(E e) {
  14. return offerLast(e);
  15. }
  16. public E remove() {
  17. return removeFirst();
  18. }
  19. public E poll() {
  20. return pollFirst();
  21. }
  22. public E element() {
  23. return getFirst();
  24. }
  25. public E peek() {
  26. return peekFirst();
  27. }

10、ArrayDeque-获取元素

  1. public E getFirst() {
  2. E x = elements[head];
  3. if (x == null)
  4. throw new NoSuchElementException();
  5. return x;
  6. }
  7. public E getLast() {
  8. E x = elements[(tail - 1) & (elements.length - 1)]; // 处理临界情况(当tail为0时),与后的结果为elements.length - 1。
  9. if (x == null)
  10. throw new NoSuchElementException();
  11. return x;
  12. }
  13. public E peekFirst() {
  14. return elements[head]; // elements[head] is null if deque empty
  15. }
  16. public E peekLast() {
  17. return elements[(tail - 1) & (elements.length - 1)];
  18. }

11、ArrayDeque-Object方法

  1. public ArrayDeque<E> clone() {
  2. try {
  3. ArrayDeque<E> result = (ArrayDeque<E>) super.clone();
  4. result.elements = Arrays.copyOf(elements, elements.length); // 深度复制。
  5. return result;
  6. } catch (CloneNotSupportedException e) {
  7. throw new AssertionError();
  8. }
  9. }
  1. https://blog.csdn.net/ztx114/article/details/89712772
  2. https://www.cnblogs.com/chenglc/p/10722304.html
  3. https://www.pdai.tech/md/java/collection/java-collection-Queue&Stack.html
  4. https://www.cnblogs.com/lxyit/p/9080590.html