PriorityQueue优先队列

PriorityQueue即优先队列,是一个无界限队列。基于优先级堆,使用平衡二叉堆实现,底层体现是数组

transient Object[] queue; // non-private to simplify nested class access

引用java8源码中PriorityQueue的类注释来说:

其队列中所有元素都按照它们的自然排序方法**(实现Comparable接口),或通过在创建对象时传入比较器(Comparator)**完成排序。

但在两种方式同时存在的情况下:优先使用Comparator比较器

//源码
private void siftDown(int k, E x) {
    if (comparator != null)    //如果比较器不存在,则使用自然排序
        siftDownUsingComparator(k, x);
    else
        siftDownComparable(k, x);
}



private void siftUp(int k, E x) {
    if (comparator != null)    //如果比较器不存在,则使用自然排序
        siftUpUsingComparator(k, x);
    else
        siftUpComparable(k, x);
}

该队列中元素不允许为null,否则将会抛出NullPointerException

//源码
public boolean offer(E e) {
    if (e == null)    //判空
        throw new NullPointerException();
    modCount++;
    int i = size;
    if (i >= queue.length)
        grow(i + 1);
    size = i + 1;
    if (i == 0)
        queue[0] = e;
    else
        siftUp(i, e);
    return true;
}

该队列中不允许不支持排序的对象进入,否则将会抛出ClassCastException

//源码
public boolean offer(E e) {
    if (e == null)
        throw new NullPointerException();
    modCount++;
    int i = size; //第一次插入时 size = 0 所以 i = 0
    if (i >= queue.length)
        grow(i + 1);
    size = i + 1;
    if (i == 0)   //故第一次插入时不会执行`else`代码块进行重排序
        queue[0] = e;
    else
        siftUp(i, e);  //当插入第二个元素时开始调用
    return true;
}

private void siftUp(int k, E x) {  //假设无自然排序 且 无比较器
    if (comparator != null)    //false
        siftUpUsingComparator(k, x);
    else
        siftUpComparable(k, x);  //使用自然排序
}

@SuppressWarnings("unchecked")
private void siftUpComparable(int k, E x) {
    Comparable<? super E> key = (Comparable<? super E>) x;  //由于没有自然排序,则转换时会抛出 `ClassCastException`
    while (k > 0) {
        int parent = (k - 1) >>> 1;
        Object e = queue[parent];
        if (key.compareTo((E) e) >= 0)
            break;
        queue[k] = e;
        k = parent;
    }
    queue[k] = key;
}

该队列的头部存放队列中最小元素,若存在多个最小元素,则头部为其中任意一个最小元素。

poll()remove()peek()element()都是通过访问头部元素而实现的。

//源码
public E peek() {
    return (size == 0) ? null : (E) queue[0];  //说句题外话,有人知道评论告诉我!
    //为什么这里要判断size == 0? 直接返回(E) queue[0],不也是同样的效果吗?
}

该队列是无界的,但同时又是有容量的,默认大小11。在元素添加的过程中,队列会扩容,在任意时间其最小大小和size一样大。

  • size:队列内存储元素个数。

扩容源码:

//源码
private void grow(int minCapacity) {
    int oldCapacity = queue.length;
    // Double size if small; else grow by 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);
}

当前容量< 64时,扩容原有容量大小 + 2个空间,则空间将变为原有容量 + 原有容量 + 2,即2 * 原有容量

当前容量>= 64时,扩容原有容量二进制表示 向右偏移 一个进制即一半的空间,则扩容后容量变成150%

该队列虽然通过上文提及到的两种方式完成排序,且也使用私有内部类实现了Iterator<E>接口,但队列并没有完成迭代器级别的排序。

因为,队列仅在插入和取出元素时才进行排序计算,且仅仅在排序规则上保证头部为最小元素。

所以想要依次按照大小取出元素不能使用迭代器遍历,而是要通过依次取出头部元素的方式来完成。

//y
/**
     * Returns an iterator over the elements in this queue. The iterator
     * does not return the elements in any particular order.
     *
     * @return an iterator over the elements in this queue
     */
    public Iterator<E> iterator() {
        return new Itr();
    }

    private final class Itr implements Iterator<E> {
        //...
    }

该队列是非同步的,多线程环境中不应该访问和修改同一个队列实例。

相反,使用线程安全的PriorityBlockingQueue可以解决这个问题。

image-20210906173750196

该线程提供时间复杂度为O(log(n))的入列和出列方法。

提供时间复杂度为O(n)remove(obj)contains(obj)方法。

以及恒定时间O(1)的查看方法,peek()element()size()

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值