PriorityQueue源码解析
PriorityQueue
PriorityQueue
基于小根堆
实现,是一个无界队列,不允许null元素。底层存储使用数组,索引n的元素的左右两个孩子索引分别为2n + 1
和2n + 2
。
PriorityQueue
元素通过比较器排序,如果比较器为空,则使用自然排序。
PriorityQueue
默认容量大小为11,当存储数组中总元素个数等于数组长度时,触发扩容。扩容时,如果原存储数组长度小于64,则扩容为原来的1倍,否则扩容50%,扩容中使用Arrays.copyOf
复制旧数组元素到新数组。
关键属性
// 默认数组容量11 private static final int DEFAULT_INITIAL_CAPACITY = 11; // 存储队列元素 queue[n]的左右孩子分别为queue[2n + 1]、queue[2(n+1)] transient Object[] queue; // 优先队列中总元素个数 private int size = 0; // 排序比较器 private final Comparator<? super E> comparator; // 结构性修改次数 transient int modCount = 0;
构造函数
/** * 使用默认容量(11)、元素之间使用自然排序 */ public PriorityQueue() { this(DEFAULT_INITIAL_CAPACITY, null); } /** * 指定初始容量,元素之间使用自然排序 */ public PriorityQueue(int initialCapacity) { this(initialCapacity, null); } /** * 指定初始容量,指定比较器 */ public PriorityQueue(int initialCapacity, Comparator<? super E> comparator) { if (initialCapacity < 1) throw new IllegalArgumentException(); // 初始存储数组,大小为initialCapacity this.queue = new Object[initialCapacity]; this.comparator = comparator; }
新增元素
public boolean add(E e) { return offer(e); } public boolean offer(E e) { if (e == null) throw new NullPointerException(); // 结构性修改次数+1 modCount++; int i = size; // 如果总元素个数=存储数组长度,触发扩容 if (i >= queue.length) grow(i + 1); size = i + 1; // (第一次插入元素)如果队列中无元素,直接设置数组0位元素为新元素 if (i == 0) queue[0] = e; else // 将新增元素添加到队列中 siftUp(i, e); return true; } /** * 添加元素至队列,上浮 * @param k 队列中总元素个数 * @param x 新增元素 */ private void siftUp(int k, E x) { if (comparator != null) siftUpUsingComparator(k, x); else siftUpComparable(k, x); } /** * 使用自然排序排列 * 插入元素时,先将新增元素放在最后一个元素位置,然后将新增元素与父级元素比较,如果大于等于父级元素,新增元 * 素便插入在最后一个位置;否则循环向上查找父节点,直到新增元素不小于父节点 * @param k 队列中总元素个数 * @param x 新增元素 */ @SuppressWarnings("unchecked") private void siftUpComparable(int k, E x) { Comparable<? super E> key = (Comparable<? super E>) x; while (k > 0) { // 找到索引为k的节点的父节点 int parent = (k - 1) >>> 1; Object e = queue[parent]; // 新增节点大于等于父节点,不需要再调整 if (key.compareTo((E) e) >= 0) break; // 将k位置设置为父节点 queue[k] = e; // 搜索节点上浮 k = parent; } // 上浮到k queue[k] = key; } @SuppressWarnings("unchecked") 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; }
扩容
/** * 扩容。新建数组,将数组扩容后,复制旧数组元素到新数组中 * 当总元素个数等于数组长度时,触发扩容 * 当数组长度小于64时,扩容一倍;否则扩容50% */ private void grow(int minCapacity) { int oldCapacity = queue.length; // Double size if small; else grow by 50% 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; }
获取元素
/** * 获取元素。取出根节点(数组的第一个元素) * 取出第一个元素后,将最后一个元素移动到index=0位置,然后与index = 1、index = 2 位置的元素比较,如果 * 最后一个元素比两个孩子大,则找出左右孩子中较小的,作为根节点。 */ public E poll() { // 数组为空,返回null if (size == 0) return null; // 队列总元素-1 int s = --size; // 结构性修改次数+1 modCount++; // 获取数组第一个元素 E result = (E) queue[0]; // 最后一个元素 E x = (E) queue[s]; queue[s] = null; // 下沉最后一个元素 if (s != 0) siftDown(0, x); return result; } /** * 下沉元素 * @param k 0 * @param x 最后一个元素 */ private void siftDown(int k, E x) { if (comparator != null) siftDownUsingComparator(k, x); else siftDownComparable(k, x); } /** * 下沉元素 * @param k 0 * @param x 最后一个元素 */ private void siftDownComparable(int k, E x) { Comparable<? super E> key = (Comparable<? super E>)x; // 由于index = k位置的元素的左右孩子在2n + 1 和 2(n + 1)位置, // 所以,如果k 大于 总元素数量的一半,那么该位置的元素也就不存在孩子节点,不需要调整 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; // 最后一个节点比index = k 的左右节点大,那么左右节点中的较小者上浮做父节点,继续下沉最后一个节点 // 较小孩子上浮 queue[k] = c; // 最后一个节点继续下城 k = child; } // 最后一个节点下沉到k位置 queue[k] = key; } @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; if (right < size && comparator.compare((E) c, (E) queue[right]) > 0) c = queue[child = right]; if (comparator.compare(x, (E) c) <= 0) break; queue[k] = c; k = child; } queue[k] = x; }
移除元素
/** * 移除指定元素 * 如果存在指定元素,返回true,否则,返回false */ public boolean remove(Object o) { // 获取指定元素在数组中位置 int i = indexOf(o); // 不存在指定元素,返回false if (i == -1) return false; // 存在指定元素,移除,并返回true 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; } // 不存在相同元素,返回-1 return -1; } /** * 移除指定下标的元素 */ private E removeAt(int i) { // assert i >= 0 && i < size; // 结构性修改+1 modCount++; // 总元素个数-1 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) { siftUp(i, moved); if (queue[i] != moved) return moved; } } return null; }
相关推荐
源码物语 2020-07-18
瓜牛呱呱 2020-11-12
柳木木的IT 2020-11-04
yifouhu 2020-11-02
lei0 2020-11-02
源码zanqunet 2020-10-28
源码zanqunet 2020-10-26
一叶梧桐 2020-10-14
码代码的陈同学 2020-10-14
lukezhong 2020-10-14
lzzyok 2020-10-10
anchongnanzi 2020-09-21
clh0 2020-09-18
changcongying 2020-09-17
星辰大海的路上 2020-09-13
abfdada 2020-08-26
mzy000 2020-08-24
shenlanse 2020-08-18
zhujiangtaotaise 2020-08-18