JDK中优先级队列PriorityQueue实现分析

JDK中优先级队列PriorityQueue实现分析

我们知道,堆可以实现优先级队列。
优先级队列可以实现以下功能:

  • 插入一个数值
  • 取出最小的数值(获得数值,并且删除)

我们来看看JDK源码中的PriorityQueue的实现。
首先看一下注释的介绍:

A PriorityQueue holds elements on a priority heap, which orders the elements according to their natural order or according to the comparator specified at construction time. If the queue uses natural ordering, only elements that are comparable are permitted to be inserted into the queue.
The least element of the specified ordering is stored at the head of the
queue and the greatest element is stored at the tail of the queue.
A PriorityQueue is not synchronized. If multiple threads will have to access it concurrently, use the java.util.concurrent.PriorityBlockingQueue.

大意是:

PriorityQueue在一个优先级堆上存储元素,元素是自然顺序或者根据构造时指定的comparator的顺序。如果队列使用自然顺序,只有可以比较的元素才能插入到队列中。
指定顺序的最小元素存在队列头部,最大的元素存在队列的尾部。
PriorityQueue不是同步的。如果多线程并发访问,要使用java.util.concurrent.PriorityBlockingQueue类。


PriorityQueue是一个泛型类,继承自AbstractQueue。

public class PriorityQueue<E> extends AbstractQueue<E> implements Serializable {

PriorityQueue默认容量大小为11

private static final int DEFAULT_CAPACITY = 11;

默认扩容率是2,在数组空间不足时扩容

private static final int DEFAULT_CAPACITY_RATIO = 2;

PriorityQueue使用一个数组存储元素,数组默认大小为队列的缺省容量大小

private transient E[] elements;
elements = newElementArray(initialCapacity);

关于优先级队列的实现,我们重点关注以下几个public方法:

  • offer: 插入元素
    public boolean offer(E o) {
        if (o == null) {
            throw new NullPointerException();
        }
        growToSize(size + 1);
        elements[size] = o;
        siftUp(size++);
        return true;
    }

实现步骤是先判断对象是否为null,如果为null则抛出空指针异常,然后确保存储元素的数组的大小,如果数组大小不够,就按照DEFAULT_CAPACITY_RATIO扩容,创建新数组,并拷贝原来数组元素进去。接着将对象存入数组最后,并使用siftUp方法重建堆,保证堆的性质,即从最后一个节点开始不断上下交换元素,直到没有上下颠倒为止。最后队列长度加一。

  • poll:获取并删除队首
    public E poll() {
        if (isEmpty()) {
            return null;
        }
        E result = elements[0];
        removeAt(0);
        return result;
    }

实现步骤是先判断队列是否为空,如果为空直接返回null,否则返回数组的第一个元素,并删除该元素。

  • peek:获取但不删除队首
    public E peek() {
        if (isEmpty()) {
            return null;
        }
        return elements[0];
    }

实现与poll基本一致,只是缺少removeAt指定位置元素的操作。

  • remove:删除指定对象
    public boolean remove(Object o) {
        if (o == null) {
            return false;
        }
        for (int targetIndex = 0; targetIndex < size; targetIndex++) {
            if (o.equals(elements[targetIndex])) {
                removeAt(targetIndex);
                return true;
            }
        }
        return false;
    }

实现步骤是先判断元素是否为null,如果是则返回false,表示对象不在队列中。如果不为null则遍历数组进行对比,直到找到对象或者遍历完为止。

  • add:添加指定对象到优先级队列
    @Override
    public boolean add(E o) {
        return offer(o);
    }

显而易见,直接调用offer方法。

  • removeAt:删除指定位置的元素
    private void removeAt(int index) {
        size--;
        elements[index] = elements[size];
        siftDown(index);
        elements[size] = null;
    }

实现步骤是先size减一,然后交换该位置元素到最后,使用siftDown对原来的位置进行不断的下降处理,知道不再上下颠倒位置,保持堆的性质,然后将最后的元素设置为null。


另外以下两个private方法,这两个方法是堆的操作,用于保持堆的性质,分别是:

  • siftUp 提升指定的元素,在插入元素的时候会用到
    private void siftUp(int childIndex) {
        E target = elements[childIndex];
        int parentIndex;
        while (childIndex > 0) {
            parentIndex = (childIndex - 1) / 2;
            E parent = elements[parentIndex];
            if (compare(parent, target) <= 0) {
                break;
            }
            elements[childIndex] = parent;
            childIndex = parentIndex;
        }
        elements[childIndex] = target;
    }
  • siftDown 将指定元素下降,在删除指定元素的时候会用到
    private void siftDown(int rootIndex) {
        E target = elements[rootIndex];
        int childIndex;
        while ((childIndex = rootIndex * 2 + 1) < size) {
            if (childIndex + 1 < size
                    && compare(elements[childIndex + 1], elements[childIndex]) < 0) {
                childIndex++;
            }
            if (compare(target, elements[childIndex]) <= 0) {
                break;
            }
            elements[rootIndex] = elements[childIndex];
            rootIndex = childIndex;
        }
        elements[rootIndex] = target;
    }

另外,堆与优先级队列在《算法导论》中都有讲到,优先级队列相关的题目如POJ2431,大家可以参考。

阅读更多
个人分类: Java
上一篇E-iceblue的Office控件试用
下一篇【LeetCode】LeetCode第一阶段50题小结
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭