拆行解码 Java 集合源码之 PriorityQueue

Java 集合源码解析系列:

特性
  • 不允许 null。

  • 基于小顶堆的无界优先级队列,顶=队头。

  • 排序基于元素可比较(要么实现 Comparable;要么指定 Comparator)

  • 不保证迭代器以特定顺序遍历元素

    顺序遍历可以使用 Arrays.sort(pq.toArray())

  • 线程不安全,同步可使用 PriorityBlockingQueue

  • 效率

    入队和出队方法( offer、poll、remove、add )提供 O(log(n)) 时间;

    remove(Object) 和 contains(Object) 方法的线性时间;

    固定时间的检索方法( peek、element、size )。

源码

初始容量为 11,可指定容量(不能小于 1)。

元素必须实现 Comparable,或队列构造函数指定 Comparator。

扩容:小于 64 时, 每次扩容翻倍再 + 2; 否则 50% 扩容。

构造函数

数据结构是 Object[],容量见上。

内部是通过小顶堆实现的,因此对于元素或比较器有要求。

在从有序集合迁移时,是不会改动原顺序的;否则根据规则重新构建小顶堆。

Ps:上述说到的小顶,都是根据 Comparable 或 Comparator 的比较值来判断的,不代表某种自然意义上的比较。

比如 2比1大这种。

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

    private static final int DEFAULT_INITIAL_CAPACITY = 11;

    /**
     * 表示为平衡二进制堆的优先级队列:queue [n]的两个子级是queue [2 * n + 1]和queue [2 *(n + 1)]。
     * 如果比较器为null,则按比较器或元素的自然顺序对优先级队列进行排序:对于堆中的每个节点n和n的每个后代d,n <= d。
     * 假定队列为非空,则具有最低值的元素位于queue [0]中
     */
    transient Object[] queue; // non-private to simplify nested class access

    int size;
    // 指定的比较器, 即使元素实现了 Comparable, 也优先使用该比较器
    private final Comparator<? super E> comparator;

    transient int modCount;     // non-private to simplify nested class access

    public PriorityQueue() {
        this(DEFAULT_INITIAL_CAPACITY, null);
    }

    public PriorityQueue(int initialCapacity) {
        this(initialCapacity, null);
    }

    public PriorityQueue(Comparator<? super E> comparator) {
        this(DEFAULT_INITIAL_CAPACITY, comparator);
    }

    public PriorityQueue(int initialCapacity,
                         Comparator<? super E> comparator) {
        // 指定初始容量至少为 1, 本身是为了兼容 1.5
        if (initialCapacity < 1)
            throw new IllegalArgumentException();
        this.queue = new Object[initialCapacity];
        this.comparator = comparator;
    }

    /**
     * 如果是 SortedSet 或 PriorityQueue 的有序集合, 则按照按照原顺序迁移
     * 否则按照元素的 Comparable 构建小顶堆
     */
    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);
        }
    }

    public PriorityQueue(PriorityQueue<? extends E> c) {
        this.comparator = (Comparator<? super E>) c.comparator();
        initFromPriorityQueue(c);
    }

    public PriorityQueue(SortedSet<? extends E> c) {
        this.comparator = (Comparator<? super E>) c.comparator();
        initElementsFromCollection(c);
    }

    /**
     * 确保queue[0]存在,以帮助peek() 和poll()
     * */
    private static Object[] ensureNonEmpty(Object[] es) {
        return (es.length > 0) ? es : new Object[1];
    }

    private void initFromPriorityQueue(PriorityQueue<? extends E> c) {
        if (c.getClass() == PriorityQueue.class) {
            this.queue = ensureNonEmpty(c.toArray());
            this.size = c.size();
        } else {
            initFromCollection(c);
        }
    }

    private void initElementsFromCollection(Collection<? extends E> c) {
        Object[] es = c.toArray();
        int len = es.length;
        // If c.toArray incorrectly doesn't return Object[], copy it.
        if (es.getClass() != Object[].class)
            es = Arrays.copyOf(es, len, Object[].class);
        if (len == 1 || this.comparator != null)
            for (Object e : es)
                if (e == null)
                    // 相当于说已排序的, 则校验元素不为 null
                    throw new NullPointerException();
                // 否则,后续构建小顶堆期间, 进行自身比较时会限制不为 null
        this.queue = ensureNonEmpty(es);
        this.size = len;
    }
    
    private void initFromCollection(Collection<? extends E> c) {
        initElementsFromCollection(c);
        heapify();
    }
    /**
     * 构建小顶堆
     * 不同于堆排序, 这里只需要构建堆即可。不需要得到整体有序的集合
     */
    private void heapify() {
        final Object[] es = queue;
        int n = size, i = (n >>> 1) - 1;
        final Comparator<? super E> cmp;
        // 大值往下筛
        if ((cmp = comparator) == null)
            for (; i >= 0; i--)
                siftDownComparable(i, (E) es[i], es, n);
        else
            for (; i >= 0; i--)
                siftDownUsingComparator(i, (E) es[i], es, n, cmp);
    }
}

heapify() 是构建小顶堆的逻辑,先按下不表,等会一起看看。

单个数据操作
public boolean add(E e) {
    return offer(e);
}

public boolean offer(E e) {
    if (e == null)
        throw new NullPointerException();
    modCount++;
    int i = size;
    if (i >= queue.length)
        grow(i + 1);
    siftUp(i, e);
    size = i + 1;
    return true;
}

public E peek() {
    return (E) queue[0];
}

public boolean remove(Object o) {
    int i = indexOf(o);
    if (i == -1)
        return false;
    else {
        removeAt(i);
        return true;
    }
}

void removeEq(Object o) {
    final Object[] es = queue;
    for (int i = 0, n = size; i < n; i++) {
        if (o == es[i]) {
            removeAt(i);
            break;
        }
    }
}

E removeAt(int i) {
    // assert i >= 0 && i < size;
    final Object[] es = queue;
    modCount++;
    int s = --size;
    if (s == i) // removed last element
        es[i] = null;
    else {
        E moved = (E) es[s];
        // i 移除后, 将最后一位填充到 i,再调整堆
        // 粗糙来看, 相当于数组全部前移一位, 所以 size - 1 的位置置为 null
        es[s] = null;
        // 因为是从最后一位调上来, 所以优先向下沉(认为它是小于当前位置的子级)
        siftDown(i, moved);
        if (es[i] == moved) {
            // 没有发生调整, 再往上调整(可能这块子树的值都比较小)
            siftUp(i, moved);
            if (es[i] != moved)
                // 因为调整, 所以在迭代器顺序遍历的情况下, 可能导致moved无法被遍历到了。
                // 所以返回给调用方处理(放在新的顺序队列(ArrayQueue)中, 原队列遍历完, 继续遍历新队列)
                return moved;
        }
    }
    return null;
}

public E poll() {
    final Object[] es;
    final E result;

    if ((result = (E) ((es = queue)[0])) != null) {
        modCount++;
        final int n;
        final E x = (E) es[(n = --size)];
        es[n] = null;
        if (n > 0) {
            final Comparator<? super E> cmp;
            // 最后一位放到堆顶, 所以不同于移除指定位(先向下或向上),这里只需要向下判断调整即可
            if ((cmp = comparator) == null)
                siftDownComparable(0, x, es, n);
            else
                siftDownUsingComparator(0, x, es, n, cmp);
        }
    }
    return result;
}
堆调整

可以看出,不论是堆构建,还是增删元素,肯定是涉及到堆的调整。

主要是两个:

  • siftUp:元素向父级比较,调整。
    • 队尾入(queue[size - 1]),所以需要向上。
  • siftDown:元素向子级(左右取小)比较,调整。
    • 队头出(queue[0]),所以需要向下。

移除中间位置的元素,从最后一位顶替,普遍认为依旧是较小的值,所以优先 siftDown,没变化再 siftUp。

根据比较方式的不同,也会有两个方法 xxxComparable 或 xxxUsingComparator。

    /**
     * 将某个元素提升到父级,甚至到根
     */
    private void siftUp(int k, E x) {
        if (comparator != null)
            siftUpUsingComparator(k, x, queue, comparator);
        else
            siftUpComparable(k, x, queue);
    }

    private static <T> void siftUpComparable(int k, T x, Object[] es) {
        Comparable<? super T> key = (Comparable<? super T>) x;
        while (k > 0) {
            // parent 就是指定插入位置的父级
            int parent = (k - 1) >>> 1;
            Object e = es[parent];
            if (key.compareTo((T) e) >= 0)
                break;
            // 父级换下来
            es[k] = e;
            // 继续往上比较
            k = parent;
        }
        es[k] = key;
    }

    private static <T> void siftUpUsingComparator(
        int k, T x, Object[] es, Comparator<? super T> cmp) {
        while (k > 0) {
            int parent = (k - 1) >>> 1;
            Object e = es[parent];
            if (cmp.compare(x, (T) e) >= 0)
                break;
            es[k] = e;
            k = parent;
        }
        es[k] = x;
    }

    /**
     * 某个元素下沉到子级,甚至是叶子节点
     */
    private void siftDown(int k, E x) {
        if (comparator != null)
            siftDownUsingComparator(k, x, queue, size, comparator);
        else
            siftDownComparable(k, x, queue, size);
    }

    private static <T> void siftDownComparable(int k, T x, Object[] es, int n) {
        // assert n > 0;
        Comparable<? super T> key = (Comparable<? super T>)x;
        int half = n >>> 1;           // loop while a non-leaf
        while (k < half) {
            // 优先取"左子"
            int child = (k << 1) + 1; // assume left child is least
            Object c = es[child];
            int right = child + 1;
            if (right < n &&
                ((Comparable<? super T>) c).compareTo((T) es[right]) > 0)
                // 左右双子中, 取小的那个
                c = es[child = right];
            if (key.compareTo((T) c) <= 0)
                break;
            // 子比父小
            es[k] = c;
            // 一直往树底推, 直到叶子
            k = child;
        }
        es[k] = key;
    }

    private static <T> void siftDownUsingComparator(
        int k, T x, Object[] es, int n, Comparator<? super T> cmp) {
        // assert n > 0;
        int half = n >>> 1;
        while (k < half) {
            int child = (k << 1) + 1;
            Object c = es[child];
            int right = child + 1;
            if (right < n && cmp.compare((T) c, (T) es[right]) > 0)
                c = es[child = right];
            if (cmp.compare(x, (T) c) <= 0)
                break;
            es[k] = c;
            k = child;
        }
        es[k] = x;
    }
批移除
	public boolean removeIf(Predicate<? super E> filter) {
        Objects.requireNonNull(filter);
        return bulkRemove(filter);
    }

    public boolean removeAll(Collection<?> c) {
        Objects.requireNonNull(c);
        return bulkRemove(e -> c.contains(e));
    }

    public boolean retainAll(Collection<?> c) {
        Objects.requireNonNull(c);
        return bulkRemove(e -> !c.contains(e));
    }

    // A tiny bit set implementation

    private static long[] nBits(int n) {
        return new long[((n - 1) >> 6) + 1];
    }
    private static void setBit(long[] bits, int i) {
        bits[i >> 6] |= 1L << i;
    }
    private static boolean isClear(long[] bits, int i) {
        return (bits[i >> 6] & (1L << i)) == 0;
    }

    /** Implementation of bulk remove methods. */
    private boolean bulkRemove(Predicate<? super E> filter) {
        final int expectedModCount = ++modCount;
        final Object[] es = queue;
        final int end = size;
        int i;
        // Optimize for initial run of survivors
        for (i = 0; i < end && !filter.test((E) es[i]); i++)
            ;
        if (i >= end) {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            return false;
        }
        // 借助位图, 批量查找符合条件的元素, 批量删除.
        // 避免多次调整
        final int beg = i;
        final long[] deathRow = nBits(end - beg);
        deathRow[0] = 1L;   // set bit 0
        for (i = beg + 1; i < end; i++)
            if (filter.test((E) es[i]))
                setBit(deathRow, i - beg);
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        int w = beg;
        for (i = beg; i < end; i++)
            if (isClear(deathRow, i - beg))
                es[w++] = es[i];
        for (i = size = w; i < end; i++)
            es[i] = null;
        // 重新构建堆
        heapify();
        return true;
    }
扩容
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

private void grow(int minCapacity) {
    int oldCapacity = queue.length;
    // Double size if small; else grow by 50%
    // 小于 64 时, 每次扩容翻倍再 + 2; 否则 50% 扩容
    int newCapacity = oldCapacity + ((oldCapacity < 64) ?
                                     (oldCapacity + 2) :
                                     (oldCapacity >> 1));
    // overflow-conscious code
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    queue = Arrays.copyOf(queue, newCapacity);
}

private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}
迭代器

迭代器允许移除元素。但是移除元素,会导致堆调整。

在最后一个元素顶替位置时,可能发生向上调整到已访问的位置上,所以 removeAt 需要返回这个发生调整的元素。

迭代器需要借助新的容器来继续遍历这部分元素,甚至移除操作。

    private final class Itr implements Iterator<E> {
        /**
         * Index (into queue array) of element to be returned by
         * subsequent call to next.
         */
        private int cursor;

        /**
         * Index of element returned by most recent call to next,
         * unless that element came from the forgetMeNot list.
         * Set to -1 if element is deleted by a call to remove.
         */
        private int lastRet = -1;

        /**
         * 由于迭代过程中“不幸的”元素删除而导致的元素队列从堆的未访问部分移至已访问部分。
         * (不幸的元素删除是那些从下移到上面的, 即siftUp上去的)
         * 我们必须访问此列表中的所有元素以完成迭代。
         * 我们在完成“常规”迭代之后执行此操作(即把原来的迭代器跑完)。
         * 我们希望大多数迭代,即使是涉及删除的迭代,也不需要在此字段中存储元素。
         * 是一个顺序双向队列
         */
        private ArrayDeque<E> forgetMeNot;

        /**
         * Element returned by the most recent call to next iff that
         * element was drawn from the forgetMeNot list.
         * 类同于 lastRet, 不过来自于新队列 forgetMeNot, 所以不是根据索引存储.
         */
        private E lastRetElt;

        /**
         * The modCount value that the iterator believes that the backing
         * Queue should have.  If this expectation is violated, the iterator
         * has detected concurrent modification.
         */
        private int expectedModCount = modCount;

        Itr() {}                        // prevent access constructor creation

        public boolean hasNext() {
            // 同时判断两个队列
            return cursor < size ||
                (forgetMeNot != null && !forgetMeNot.isEmpty());
        }

        public E next() {
            if (expectedModCount != modCount)
                throw new ConcurrentModificationException();
            if (cursor < size)
                return (E) queue[lastRet = cursor++];
            if (forgetMeNot != null) {
                lastRet = -1;
                // 从新队列中取
                lastRetElt = forgetMeNot.poll();
                if (lastRetElt != null)
                    return lastRetElt;
            }
            throw new NoSuchElementException();
        }

        public void remove() {
            if (expectedModCount != modCount)
                throw new ConcurrentModificationException();
            if (lastRet != -1) {
                E moved = PriorityQueue.this.removeAt(lastRet);
                // 由于最近遍历被移除了, 所以置为 -1
                lastRet = -1;
                if (moved == null)
                    // 移除后的补位机制, 所以可以重新访问当前位置
                    cursor--;
                else {
                    // moved 不为空, 说明发生了向上调整
                    if (forgetMeNot == null)
                        // 是一个顺序双向队列
                        forgetMeNot = new ArrayDeque<>();
                    forgetMeNot.add(moved);
                }
            } else if (lastRetElt != null) {
                // 从新队列中移除
                PriorityQueue.this.removeEq(lastRetElt);
                lastRetElt = null;
            } else {
                throw new IllegalStateException();
            }
            expectedModCount = modCount;
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值