【Java集合】Queue源码

1. ArrayDeque

ArrayDeque,数组双端队列(具有栈的特点),实现了Deque(Queue)接口,线程不安全。

ArrayDeque内部结构是一个循环数组,它的成员变量如下:

    transient Object[] elements; 

    transient int head;

    transient int tail;

    private static final int MIN_INITIAL_CAPACITY = 8;

它的扩容操作如下(通过移位或的操作进行放大倍数,使得数组的容量值不必严格满足2的幂):

    // 初始化时分配数组大小
    private static int calculateSize(int numElements) {
        int initialCapacity = MIN_INITIAL_CAPACITY;
        // 最佳匹配元素与2的乘积
        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;
    }
    
    // 新增元素时扩容
    public void addFirst(E e) {
        if (e == null)
            throw new NullPointerException();
        elements[head = (head - 1) & (elements.length - 1)] = e;
        if (head == tail)
            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
        int newCapacity = n << 1;
        if (newCapacity < 0)
            throw new IllegalStateException("Sorry, deque too big");
        Object[] a = new Object[newCapacity];
        System.arraycopy(elements, p, a, 0, r);
        System.arraycopy(elements, 0, a, r, p);
        elements = a;
        head = 0;
        tail = n;
    }

ArrayDeque的UML类图如下:

2. PriorityQueue

PriorityQueue队列使用的是最小堆结构,可用于排序。

它的成员变量与其中之一的构造方法如下:

public class PriorityQueue<E> extends AbstractQueue<E> 
                              implements java.io.Serializable {

    private static final int DEFAULT_INITIAL_CAPACITY = 11;
 
    /**
     * 优先队列队列[n]有两个子队列:队列[2*n+1]和队列[2*(n+1)]。
     * 优先级队列由比较器排序,如果比较器为空,则由元素的自然顺序排序:
     * 对于堆中的每个节点n和n的每个后代d,n<=d。
     */
    transient Object[] queue; 

    private int size = 0;

    private final Comparator<? super E> comparator;

    // 优先队列已被删除的次数
    transient int modCount = 0;
    
    // 构造方法
    public PriorityQueue(Collection<? extends E> c) {
        if (c instanceof SortedSet<?>) {
            SortedSet<? extends E> ss = (SortedSet<? extends E>) c;
            this.comparator = (Comparator<? super E>) ss.comparator();
            initElementsFromCollection(ss);
        }
        else if (c instanceof PriorityQueue<?>) {
            PriorityQueue<? extends E> pq = (PriorityQueue<? extends E>) c;
            this.comparator = (Comparator<? super E>) pq.comparator();
            initFromPriorityQueue(pq);
        }
        else {
            this.comparator = null;
            initFromCollection(c);
        }
    }
    
    private void initElementsFromCollection(Collection<? extends E> c) {
        Object[] a = c.toArray();
        // If c.toArray incorrectly doesn't return Object[], copy it.
        if (a.getClass() != Object[].class)
            a = Arrays.copyOf(a, a.length, Object[].class);
        int len = a.length;
        if (len == 1 || this.comparator != null)
            for (int i = 0; i < len; i++)
                if (a[i] == null)
                    throw new NullPointerException();
        this.queue = a;
        this.size = a.length;
    }
    
    private void initFromPriorityQueue(PriorityQueue<? extends E> c) {
        if (c.getClass() == PriorityQueue.class) {
            this.queue = c.toArray();
            this.size = c.size();
        } else {
            initFromCollection(c);
        }
    }

    private void initFromCollection(Collection<? extends E> c) {
        initElementsFromCollection(c);
        heapify();
    }

}

构造方法中需要注意的是,initElementFromCollection 方法本身并不能保证初始化后queue数组元素堆化/排序,所以 initFromCollection 方法在调用完 initElementFromCollection 后进行了堆化heapify;至于传入的集合是SortedSet时,PriorityQueue会继续使用SortedSort中的Comparator对象来排序,无论怎么排序,新创建的PriorityQueue中的数据对象都会符合PriorityQueue对数据对象存储的要求。

扩容操作:

    private void grow(int minCapacity) {
        int oldCapacity = queue.length;
        // 如果原始容量小于64则双倍扩容,否则扩大1.5倍
        int newCapacity = oldCapacity + ((oldCapacity < 64) ?
                                         (oldCapacity + 2) :
                                         (oldCapacity >> 1));
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        queue = Arrays.copyOf(queue, newCapacity);
    }

优先队列的UML图如下:

优先队列的工作原理

优先队列:堆(建堆、排序,堆是一种特殊的树),为了读写平衡,PriorityQueue默认最小堆,可以通过Comparator接口干预成最大堆。该堆中有两个主要的方法:升序操作 siftUp 、降序操作 siftDown,建堆时用siftDown,添加元素时用到了siftUp。

自行理解下移除函数吧(它同时用到了siftUp与siftDown方法)。。。

    private E removeAt(int i) {
        modCount++;
        int s = --size;
        if (s == i) // removed last element
            queue[i] = null;
        else {
            E moved = (E) queue[s];
            queue[s] = null;
            siftDown(i, moved);
            if (queue[i] == moved) {
                siftUp(i, moved);
                if (queue[i] != moved)
                    return moved;
            }
        }
        return null;
    }

从队列中删除第i个元素。通常情况下,这种方法会将元素保留i-1以内不受影响。如果恰好是最好一个元素,那么移除它返回null。为了保持堆不变,它必须将列表中较后的元素替换为i元素较前的元素,这种情况下,此方法返回以前位于列表末尾的元素、现在位于i之前的某个位置。

3. 总结

ArrayDeque采用双端队列对数组进行了充分利用,PriorityQueue堆基于权值的排序性能进行了优化。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值