PriorityQueue源码分析

简介

PriorityQueue是有限队列,基于优先堆实现;

一、PriorityQueue内部结构实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
 public class PriorityQueue<E> extends AbstractQueue<E>
     implements java.io.Serializable {
 
 
     //默认初始化队列大小
     private static final int DEFAULT_INITIAL_CAPACITY = 11;
 
     /**
      * Priority queue represented as a balanced binary heap: the two
      * children of queue[n] are queue[2*n+1] and queue[2*(n+1)].  The
      * priority queue is ordered by comparator, or by the elements'
      * natural ordering, if comparator is null: For each node n in the
      * heap and each descendant d of n, n <= d.  The element with the
      * lowest value is in queue[0], assuming the queue is nonempty.
      * 优先队列表示为一个平衡的二进制堆:
      *    队列中的两个子队列[n]是队列[2 n + 1]和队列[2(n + 1)]。
      *    如果比较器是空的,那么优先队列是由比较器排序的,
      *    如果比较器是空的,那么对于堆中的每个节点n和n的每一个下降的d,n <= d。值最小的元素在队列[0]中,假设队列为非空。
      */
     private transient Object[] queue;
 
     /**
      * The number of elements in the priority queue.
      * 优先队列中的元素个数。
      */
     private int size = 0;
 
     /**
      * The comparator, or null if priority queue uses elements'
      * natural ordering.
      */
     private final Comparator<? super E> comparator;
 
     /**
      * The number of times this priority queue has been
      * <i>structurally modified</i>.  See AbstractList for gory details.
      * 修改次数
      */
     private transient int modCount = 0;

二、插入优先级队列。

1. 添加元素

  • offer
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
    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)  // i=0 根节点
            queue[0] = e;
        else
            siftUp(i, e); //往上重新调整堆结构
        return true;
    }

    private void siftUp(int k, E x) {
        if (comparator != null)
            siftUpUsingComparator(k, x);
        else
            siftUpComparable(k, x); //强转换Comparable对象做判断,具体不做分析
    }

    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;
    }    
  • add 添加
1
2
3
      public boolean add(E e) {
          return offer(e);
      }
  • 总结
  • 以数据实现树形结构,就满足树形结构特性;
    • n节点,子节点分左右;左节点2n+1,右节点2n+2
    • n节点是子节点,父节点n/2,更好性能n »> 1;
  • 新元素添加到最后,往父节点左判断大小,小一直往改判断节点父节点判断大小;一直到跟节点结束;只有中间判断为大,停止操作;

2. 扩容(grow)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
    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%  // oldCapacity大于64 扩容50%
                                                    // oldCapacity小于64 扩容2倍+2                                   
         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;
     }

三、poll 出队列

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
   // 先出第个元素,把最后元素体值给第一个元素,进行往下重新调整堆结构
    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 siftDown(int k, E x) {
        if (comparator != null)
            siftDownUsingComparator(k, x);
        else
            siftDownComparable(k, x);
    }

    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 left节点
            Object c = queue[child]; 
            int right = child + 1;   // right 节点
            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;
    }   
  • poll就是删除下标为0的数据;返回当前的数据;
  • 在把最后的元素放到下标为零的数组中,进行往下调整堆结构
  • 父节点跟左右节点判断大小;

四、获取头节点元素,不是删除头节点

  • peek
  • element 方法为空thorw NoSuchElementException,AbstractQueue抽象类实现
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
    public E peek() {
        if (size == 0)
            return null;
        return (E) queue[0];
    }
 
  public E element() {
      E x = peek();
      if (x != null)
          return x;
      else
          throw new NoSuchElementException();
  }

五、删除元素

1. remove() 为空thorw NoSuchElementException,AbstractQueue抽象类实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
    /**
     *
     * @return the head of this queue
     * @throws NoSuchElementException if this queue is empty
     */
    public E remove() {
        E x = poll();
        if (x != null)
            return x;
        else
            throw new NoSuchElementException();
    }

2. remove(Object o)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
  /**
     * Removes a single instance of the specified element from this queue,
     * if it is present.  More formally, removes an element {@code e} such
     * that {@code o.equals(e)}, if this queue contains one or more such
     * elements.  Returns {@code true} if and only if this queue contained
     * the specified element (or equivalently, if this queue changed as a
     * result of the call).
     *
     * @param o element to be removed from this queue, if present
     * @return {@code true} if this queue changed as a result of the call
     */
    public boolean remove(Object o) {
        int i = indexOf(o);  //查询指定元素的下标,为找到返回-1
        if (i == -1)
            return false;
        else {
            removeAt(i);
            return true;
        }
    }
   private int indexOf(Object o) {
        if (o != null) {
            for (int i = 0; i < size; i++)
                if (o.equals(queue[i]))
                    return i;
        }
        return -1;
    } 
    
    /**
     * Removes the ith element from queue.
     *
     * Normally this method leaves the elements at up to i-1,
     * inclusive, untouched.  Under these circumstances, it returns
     * null.  Occasionally, in order to maintain the heap invariant,
     * it must swap a later element of the list with one earlier than
     * i.  Under these circumstances, this method returns the element
     * that was previously at the end of the list and is now at some
     * position before i. This fact is used by iterator.remove so as to
     * avoid missing traversing elements.
     */
    private E removeAt(int i) {
        assert i >= 0 && i < size;
        modCount++;
        int s = --size;
        if (s == i) // removed last element 删除最后元素
            queue[i] = null;
        else {
            E moved = (E) queue[s]; //把最后元素替换删除元素,在重新调整堆结构
            queue[s] = null;
            siftDown(i, moved);  //往下调整
            if (queue[i] == moved) { // moved为改变 就重新调整堆结构
                siftUp(i, moved);  //往上调整
                if (queue[i] != moved) 
                    return moved;
            }
        }
        return null;
    }    

六、Iterator 迭代器实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
    private final class Itr implements Iterator<E> {
        /**
         * Index (into queue array) of element to be returned by
         * subsequent call to next.
         * 元素的索引(进入队列数组),然后在接下来的调用中返回。
         */
        private int cursor = 0;

        /**
         * 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;

        /**
         * A queue of elements that were moved from the unvisited portion of
         * the heap into the visited portion as a result of "unlucky" element
         * removals during the iteration.  (Unlucky element removals are those
         * that require a siftup instead of a siftdown.)  We must visit all of
         * the elements in this list to complete the iteration.  We do this
         * after we've completed the "normal" iteration.
         * 
         *
         * We expect that most iterations, even those involving removals,
         * will not need to store elements in this field.
         * 
         * 迭代过程中遗漏的元素,进行删除(进行siftup操作,往上重构堆位置,后面下标还没有迭代,所有保存在集合中)
         */
        private ArrayDeque<E> forgetMeNot = null;

        /**
         * Element returned by the most recent call to next iff that
         * element was drawn from the forgetMeNot list.
         */
        private E lastRetElt = null;

        /**
         * 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;

        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);
                lastRet = -1;
                if (moved == null)
                    cursor--;
                else {
                    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;
        }
    }

七、总结

  • PriorityQueue使用最小堆结构,优先队列中的[n]父节点,相应左孩子[2 n + 1],右孩子[2(n + 1)]。(n>=0),0是根节点;
  • PriorityQueue默认构造初始化队列数组11。
  • 扩容是判断容量是否小于64,是按2倍+2扩容,否按1.5倍扩容
  • 添加队列,出队列都重构建最小堆。
    • 添加队列往上重构建最小堆。
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
          private void siftUpUsingComparator(int k, E x) {
              while (k > 0) {
                  int parent = (k - 1) >>> 1;   //父亲节点 k-1 根节点0
                  Object e = queue[parent];
                  if (comparator.compare(x, (E) e) >= 0) //跟父节点比较
                      break;
                  queue[k] = e;
                  k = parent;
              }
              queue[k] = x;
          } 
    
    • 出队列,先根节点出列,在把最后节点赋值根节点,在往下重构建最小堆。
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    
        private void siftDownUsingComparator(int k, E x) {
            int half = size >>> 1;  
            while (k < half) {
                int child = (k << 1) + 1; //left节点
                Object c = queue[child];
                int right = child + 1;   //right节点
                if (right < size &&
                    comparator.compare((E) c, (E) queue[right]) > 0)  //left节点跟right节点做比较,看谁最小
                    c = queue[child = right];
                if (comparator.compare(x, (E) c) <= 0) //在跟当前x节点值做比较
                    break;
                queue[k] = c;
                k = child;
            }
            queue[k] = x;
        }