【源码系列-2】PriorityQueue

  • PriorityQueue,我用过的场景是用来构建大小堆。
  • PriorityQueue的排序是在创建时就制定的Comparable或Comparator。
  • PriorityQueue不允许添加null元素。
  • 如果队列中插入Comparable或Comparator无法比较的元素时,会抛出ClassCastException异常。
  • 队列头是队列的最后一个元素。如果最后一个值有多个元素的话,队列头是任意一个元素。
  • 队列可通过poll,remove,peek,element方法获取队列头,即队列的最后一个元素。
  • PriorityQueue的长度没有限制,内部变量capacity用于管理队列长度,当添加元素时,容量会自动增加。
  • PriorityQueue不是同步的,如果一个线程在修改PriorityQueue的话,其他线程无法获取该PriorityQueue。在这种情况下,考虑线程安全的类:java.util.concurrent.PriorityBlockingQueue
  • 时间复杂度是O(log(n))的方法:offer,poll,remove(),add
  • 时间复杂度是O(n)的方法:remove(Object),contains(Object)
  • 时间复杂度是O(1)的方法:peek,element,size

PriorityQueue实现了java.io.Serializable接口,指定了serialVersionUID

PriorityQueue声明了默认的容量DEFAULT_INITIAL_CAPACITY = 11;

transient关键字无法被序列化
final关键字:

  • 修饰类:该类不可以被继承
  • 修饰方法:该方法不可以被重写
  • 修饰变量:该变量不可以被修改
  • Comparator用private final 修饰,使得Comparator只可以在初始化时被指定,且不可以在其他时候修改。
    private final Comparator<? super E> comparator;

  • 在初始化优先队列的容量时,最小是1,否则抛出IllegalArgumentException。

public PriorityQueue(int initialCapacity,
                         Comparator<? super E> comparator) {
        // Note: This restriction of at least one is not actually needed,
        // but continues for 1.5 compatibility
        if (initialCapacity < 1)
            throw new IllegalArgumentException();
        this.queue = new Object[initialCapacity];
        this.comparator = comparator;
    }
  • PriorityQueue可通过Collection的实现类构建,前提是实现类是
    SortedSet的子类或者是PriorityQueue的实例,也就是本身就有comparator的实例,否则无法创建。

  • 创建实例时,将Collection的实现类使用toArray()转换为数组,并遍历,判断有没有null元素。

  • 优先队列的最大size如下:
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    以为部分虚拟机要在数组头存储东西,所以为了避免爆内存-8.

  • PriorityQueue实现的基础是平衡二叉堆,存储在数组中,元素queue[n] 的左孩子和右孩子分别为queue[2n+1] 、queue[2(n+1)]

  • 因为上述存储方式,PriorityQueue头就是0位元素,所以返回数组的第0位。
    public E peek() { return (size == 0) ? null : (E) queue[0];}

/>>>表示无符号右移,也叫逻辑右移,即若该数为正,则高位补0,而若该数为负数,则右移后高位同样补0

  • 添加元素时,用到offer函数,offer函数调用siftUpUsingComparator函数,siftUpUsingComparator函数的k是添加该元素之后的队列的长度,x是该元素,如果想在第k位存储元素,则该元素的父结点为m位的元素,2*m+1=k, m=(k- 1)/2 ,如果该元素大于等于父元素,则直接保存,否则把父元素写在当前的位置上,再找当前元素的父元素,因此offer的时间复杂度是O(log(n))。

如果调用compare方法大于0,就把前一个数和后一个数交换,也就是把大的数放后面了

private void siftUpUsingComparator(int k, E x) {
        while (k > 0) {
            int parent = (k - 1) >>> 1;
            Object e = queue[parent];
            if (comparator.compare(x, (E) e) >= 0)
                break;
            queue[k] = e;
            k = parent;
        }
        queue[k] = x;
    }
  • poll方法直接把最后一位赋为空,并传入最后一位元素到siftDown函数中。
public E poll() {
    if (size == 0)
        return null;
    int s = --size;
    modCount++;
    E result = (E) queue[0];
    E x = (E) queue[s];
    queue[s] = null;
    if (s != 0)
        siftDown(0, x);
    return result;
}
  • siftDown方法调用siftDownComparable函数,对size除2,size是原来的size0-1之后的结果,half=(size0-1)/2,即half所在的元素是第size0位的父结点。k是0,如果k小于half,则证明还没有把父结点填充上,需要获取k的左孩子c,如果左孩子大于右孩子,则把child指向右孩子,并把有孩子赋值给c用c去替换父节点,直到替换到跟节点。
private void siftDownComparable(int k, E x) {
    Comparable<? super E> key = (Comparable<? super E>)x;
    int half = size >>> 1;        // loop while a non-leaf
    while (k < half) {
        int child = (k << 1) + 1; // assume left child is least
        Object c = queue[child];
        int right = child + 1;
        if (right < size &&
            ((Comparable<? super E>) c).compareTo((E) queue[right]) > 0)
            c = queue[child = right];
        if (key.compareTo((E) c) <= 0)
            break;
        queue[k] = c;
        k = child;
    }
    queue[k] = key;
}
  • contains要遍历一边数组,所以是O(n)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值