PriorityQueue

1 初始化

PriorityQueue优先级队列,可以为队列里面的元素定义优先级别,从类定义上来看,它具备基础队列的功能,同时可以进行序列化。transient Object[] queue 存储队列元素、size(存储元素大小)、comparator(排序规则)、modCount(纪录修改的次数)

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.
     */
    transient Object[] queue; // non-private to simplify nested class access

    /**
     * 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.
     */
    transient int modCount = 0; // non-private to simplify nested class access

    /**
     * Creates a {@code PriorityQueue} with the default initial
     * capacity (11) that orders its elements according to their
     * {@linkplain Comparable natural ordering}.
     */
    public PriorityQueue() {
        this(DEFAULT_INITIAL_CAPACITY, null);
    }
}

2 入队列

offer方法主要是插入队列,基本都是普通的数组操作,这里重点的是两个方法grow和siftUp。

 public boolean offer(E e) {
        if (e == null)
            throw new NullPointerException();
        modCount++;
        int i = size;
        //判断size是否大于queue.length,如果大于就调用grow
        if (i >= queue.length)
            grow(i + 1);
        size = i + 1;
        //如果原有队列的size是0那么就直接插入,否则调用siftUp
        if (i == 0)
            queue[0] = e;
        else
            siftUp(i, e);
        return true;
}
grow方法

这里的grow跟ArrayList的grow方法不太一样,不一样的地方是增长因子不一样,这里做了个优化。只有大于64的时候采用增加50%的策略

 private void grow(int minCapacity) {
        int oldCapacity = queue.length;
        // Double size if small; else grow by 50%
        //判断当大于64的时候采用增加50%这种策略,也就是跟ArrayList一样的策略
        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);
    }
siftUp方法

添加对象的时候会调用siftUp去排序 ,这里的排序并不是真正意义上的排序,核心在(k - 1) >>> 1这个上面,这个熟悉 二叉树的人可能比较清楚,(k - 1) >>> 1是获取自己的父节点。其实这里并不是真正有序,只是保证了自己的父亲节点比自己小或者大,所以最大或者最小的值一定在最前面。

private void siftUp(int k, E x) {
  //判断是否有自定义的comparator,如果有的话就直接使用comparator去排序
  if (comparator != null)
      siftUpUsingComparator(k, x);
  else
      siftUpComparable(k, x);
}

@SuppressWarnings("unchecked")
private void siftUpComparable(int k, E x) {
  Comparable<? super E> key = (Comparable<? super E>) x;
  while (k > 0) {
      //每次-1取一半去对比,并不是排序,所以这里并不是真正意义上的排序
      int parent = (k - 1) >>> 1;
      Object e = queue[parent];
      if (key.compareTo((E) e) >= 0)
          break;
      queue[k] = e;
      k = parent;
  }
  queue[k] = key;
}

@SuppressWarnings("unchecked")
private void siftUpUsingComparator(int k, E x) {
  while (k > 0) {
      //无符号左移1位
      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;
}

这里我添加了几个数到了集合队列里面,放入前:{3,6,52,14,12,1,34,3,2,22},放入后:{1, 2, 3, 3, 6, 34, 14, 12, 52, 22},可以看到。

3 出队列

一般来说最大或者最小的数据放在队列第一个,所以只需要取下标为0的即可,然后调用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;
}

调整数组

数组这里主要是做一个调整,重新把顺序进行排序,按照之前的自定义排序,首先先拿到当前队列顶点的对象,然后拿到自己的左右及诶但

private void siftDown(int k, E x) {
    if (comparator != null)
        siftDownUsingComparator(k, x);
    else
        siftDownComparable(k, x);
}
@SuppressWarnings("unchecked")
private void siftDownUsingComparator(int k, E x) {
//数组的一半
    int half = size >>> 1;
    while (k < half) {
    //拿到子节点的索引
        int child = (k << 1) + 1;
        Object c = queue[child];
        //拿到右节点
        int right = child + 1;
        //判断左右节点的权重,如果c权重比右节点大,那么就使用右节点
        if (right < size &&
            comparator.compare((E) c, (E) queue[right]) > 0)
            c = queue[child = right];
            //判断如果X小于C那么就停止了
        if (comparator.compare(x, (E) c) <= 0)
            break;
        queue[k] = c;
        k = child;
    }
    queue[k] = x;
}

调整大概如下,可以看到2的位置变化了,3的位置也变化了,基本都是一个个子节点

[1, 2, 8, 3, 6, 34, 14, 12, 52, 22]
1
[2, 3, 8, 12, 6, 34, 14, 22, 52]

results matching ""

    No results matching ""