PriorityQueue优先队列实现原理

一、什么是优先队列

   优先队列不是按照普通对象先进先出原FIFO则进行数据操作,其中的元素有优先级属性,优先级高的元素先出队。本文提到的PriorityQueue队列,是基于最小堆原理实现。

   需要注意:PriorityQueue继承了AbstractQueue没有实现BlockingQueue接口,所以没有take阻塞方法。

二、什么是最小堆

    最小堆是一个完全二叉树,所谓的完全二叉树是一种没有空节点的二叉树。

    最小堆的完全二叉树有一个特性是根节点必定是最小节点,子女节点一定大于其父节点。还有一个特性是叶子节点数量=全部非叶子节点数量+1

    在 PriorityQueue队列中,基于数组保存了完全二叉树。所以在已知任意一个节点在数组中的位置,就可以通过一个公式推算出其左子树和右子树的下标。已知节点下标是i,那么他的左子树是2*i+1,右子树是2*i+2。


三、PriorityQueue队列的存取原理

     1、首先看下源码

    /**
     * Inserts item x at position k, maintaining heap invariant by
     * promoting x up the tree until it is greater than or equal to
     * its parent, or is the root.
     *
     * To simplify and speed up coercions and comparisons. the
     * Comparable and Comparator versions are separated into different
     * methods that are otherwise identical. (Similarly for siftDown.)
     *
     * @param k the position to fill
     * @param x the item to insert
     */
    private void siftUp(int k, E x) {
        if (comparator != null)
            siftUpUsingComparator(k, x);
        else
            siftUpComparable(k, x);
    }

   

在调用offer(E e)方法入队时,首先进行非null判断,然后是grow方法扩容,紧接着就是siftUp方法调整节点位置,那么是如何调整的呢?为什么要调整?

2、为什么要调整节点顺序?

因为这是一个最小堆,最小堆必须要严格按照子节点大于父亲节点的顺序做数组中存放。

3、如何调整?

siftup方法有个if-else判断,如果有比较器,则使用siftUpUsingComparator(k, x);如果没有则调用siftUpComparable(k, x);这个方法会默认给一个比较器。

比较什么呢??我们说最小堆实现了这个队列,队列一定有入队操作,入队是元素首先进入队列尾,然后和自己的父节点比较,像冒泡一样将该节点冒到合适的位置,即比自己的父亲节点大,比自己的儿子节点小。

 private void siftUpComparable(int k, E x) {
        Comparable key = (Comparable) x;
        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;
    }
4、如何出队

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;
    }

 private void siftDownComparable(int k, E x) {
        Comparable key = (Comparable)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) 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;
    }
出队和入队原理正好相反,每次出队也要进行siftDown操作,对元素顺序重新整理。


四、总结

PriorityQueue队列不适合进场出队入队的频繁操作,但是他的优先级特性非常适合一些对顺序有要求的数据处理场合。

你可能感兴趣的:(源码学习,内存)