jdk8源码之Queue-ArrayQueue

ArrayQueue是Java中一种基于数组实现的先进先出(FIFO)队列,常作为缓冲队列使用。本文详细介绍了ArrayQueue的初始化、offer、poll、peek、remove、add、push和spliterator等操作的源码实现,分析了其内部结构和扩容机制。此外,还探讨了ArrayQueue在数据结构变更时与快速失败策略的区别以及可能的使用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

关于队列这个数据结构,大家应该都是比较熟悉。列队是一种先进先出(FIFO)的数据结构。删除操作只能在表的头部,插入操作只能在表的尾部。
Queue一般是作为一个缓冲队列使用的,简单举例:生产端的生产速度偶尔会大于消费端的消费速度,但又不想等待对方,此时如果有个缓存队列这样的问题就解决啦。生产端生产的东西只管放入缓存队列,消费端从缓存队列里面拿东西,达到一个缓冲的目的。
图片描述
LinkedList是基于链表来实现,ArrayQueue基于数组实现。下面我们会主要讲一下这ArrayQueue者的源码实现。

ArrayQueue

public class ArrayDeque<E> extends AbstractCollection<E>
                           implements Deque<E>, Cloneable, Serializable
{
 //实际存放元素的数组
 transient Object[] elements; 
 //头指针
 transient int head;
 //尾指针
 transient int tail;
 //默认最小容量
 private static final int MIN_INITIAL_CAPACITY = 8;

 }
ArrayQueue初始化
//未制定容量时,默认为16
public ArrayDeque() {
        elements = new Object[16];
    }
//制定初始容量
public ArrayDeque(int numElements) {
        allocateElements(numElements);
    }
//指定初始值
public ArrayDeque(Collection<? extends E> c) {
        allocateElements(c.size());
        addAll(c);
    }
//指定容量小于8时,返回8
//否则,返回initialCapacity=2^n,2^n越界时,则取2^30
private void allocateElements(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
        }
        elements = new Object[initialCapacity];
    }
ArrayQueue-offer()
 //添加一个元素并返回true,如果队列已满,则返回false
 public boolean offer(E e) {
        return offerLast(e);
    }
 public boolean offerLast(E e) {
        addLast(e);
        return true;
    }
 //添加一个元素置尾端
 public void addLast(E e) {
     if (e == null)
         throw new NullPointerException();
     //添加元素置尾端
     elements[tail] = e;
     //尾指针tail+1,计算tail在elements中的位置
     //例如:数组长度8,tail+1=8,则tail=0
     //当tail==head时,即数组容量不足,进行扩容操作
     if ( (tail = (tail + 1) & (elements.length - 1)) == head)
         doubleCapacity();
  }
 //双倍扩容
 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
    //新容量=当前容量*2
    int newCapacity = n << 1;
    //新容量过大,抛错
    if (newCapacity < 0)
        throw new IllegalStateException("Sorry, deque too big");
    //创建一个新容量大小的数组
    Object[] a = new Object[newCapacity];
    //将elements拷贝置a中
    System.arraycopy(elements, p, a, 0, r);
    System.arraycopy(elements, 0, a, r, p);
    elements = a;
    head = 0;
    //注意:因为满了才进行扩容,所以tail=n
    //如果不是满了才扩容,这里会出现问题
    tail = n;
}
ArrayQueue-poll()
 //移除并返回第一个元素,如果队列为空,返回null
 public E poll() {
        return pollFirst();
    }
 public E pollFirst() {
        int h = head;
        @SuppressWarnings("unchecked")
        E result = (E) elements[h];
        // Element is null if deque empty
        if (result == null)
            return null;
        //取到后‘必须’把当前位置设为null
        elements[h] = null;     
        head = (h + 1) & (elements.length - 1);
        return result;
    }
ArrayQueue-peek()
 //返回头部元素,如果队列为空,返回null
 public E peek() {
        return peekFirst();
    }
 public E peekFirst() {
        // 如果head位置没有值,则返回null
        return (E) elements[head];
    }
 //返回头部元素,如果为null,抛错
 public E element() {
        return getFirst();
    }
 public E getFirst() {
  //获取头部元素
  @SuppressWarnings("unchecked")
  E result = (E) elements[head];
  //如果为null,抛错
  if (result == null)
      throw new NoSuchElementException();
  return result;
 }
ArrayQueue-remove(e)
   //移除头段元素并返回,如果为null,则抛错
   //对比poll()方法,多了一个判空抛错的动作
   public E remove() {
        return removeFirst();
    }
  public E removeFirst() {
       E x = pollFirst();
       if (x == null)
           throw new NoSuchElementException();
       return x;
   }
ArrayQueu-add(e)
  //增加一个元素至尾端(队列满,则扩容)
  public boolean add(E e) {
        addLast(e);
        return true;
    }
 public void addLast(E e) {
        if (e == null)
            throw new NullPointerException();
        elements[tail] = e;
        //tail后移一位
        //检测是否已满,扩容
        if ( (tail = (tail + 1) & (elements.length - 1)) == head)
            doubleCapacity();
    }
ArrayQueu-push(e)
//增加一个元素至首端(队列满,则扩容)
 public void push(E e) {
        addFirst(e);
    }
 public void addFirst(E e) {
    //e不能为空
    if (e == null)
         throw new NullPointerException();
     //heah前移一位,并设置当前位置的值
     elements[head = (head - 1) & (elements.length - 1)] = e;
     //如果队列满,则扩容
     if (head == tail)
         doubleCapacity();
    }
ArrayQueue-spliterator()

关于ArrayQueue的并行遍历器是怎么实现的

 public Spliterator<E> spliterator() {
        return new DeqSpliterator<E>(this, -1, -1);
    }
static final class DeqSpliterator<E> implements Spliterator<E> {
        private final ArrayDeque<E> deq;
        //围栏值,index能到达的最大位置
        private int fence;  // -1 表示第一次使用
        //当前位置指针
        private int index;  // current index, modified on traverse/split

        DeqSpliterator(ArrayDeque<E> deq, int origin, int fence) {
            this.deq = deq;
            this.index = origin;
            this.fence = fence;
        }
        //获取围栏值值
        private int getFence() { // force initialization
            int t;
            if ((t = fence) < 0) {
            //第一次初始化时,获取index和fence的值
                t = fence = deq.tail;
                index = deq.head;
            }
            return t;
        }
        //分割,因为head可能比tail大,因此中间位置需要计算一下
        public DeqSpliterator<E> trySplit() {
            int t = getFence(), h = index, n = deq.elements.length;
            //h == t 表示没有数据
            //(h + 1) & (n - 1)) == t 表示只有一个数据
            if (h != t && ((h + 1) & (n - 1)) != t) {
                //h > t 表示head>tail时
                if (h > t)
                    t += n;
                //计算m的位置
                //例如:n:8,head:6,tail:4
                // 则 t=4+8=12
                // m =((6+12)>>>1)&(8-1)=1
                int m = ((h + t) >>> 1) & (n - 1);
                return new DeqSpliterator<>(deq, h, index = m);
            }
            return null;
        }
        public void forEachRemaining(Consumer<? super E> consumer) {
                    if (consumer == null)
                        throw new NullPointerException();
                    Object[] a = deq.elements;
                    int m = a.length - 1, f = getFence(), i = index;
                    index = f;
                    while (i != f) {
                        @SuppressWarnings("unchecked") E e = (E)a[i];
                        i = (i + 1) & m;
                        //遍历时,发生‘结构变更’,e可能为null
                        if (e == null)
                            throw new ConcurrentModificationException();
                        consumer.accept(e);
                    }
                }
        ...
       //特征值
        public int characteristics() {
            return Spliterator.ORDERED | Spliterator.SIZED |
                Spliterator.NONNULL | Spliterator.SUBSIZED;
        }
    }

}

可以看到ArrayQueue的并行迭代器没有什么特别的,只不过我们以前看的ArrayList,HashMap等数据结构都是通过一个modCount来保证快速失败的,而ArrayQueue则是在遍历时,但它结构发生变更,并不一定会抛错!

ArrayQueue 小结

可以看到,ArrayQueue的内部是基于一个数组来实现,通过两个指针head和tail来分别指向头端和尾端,当head和tail相等时,说明容量已满,此时会进行扩容操作。
另外,可以思考一下ArrayQueue的使用场景!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值