Queue-PriorityQueue源码解析 (2)

如果构造PriorityQueue时传有特定比较器,就按特定比较器方式设置顶部元素,否则按默认自然比较器方式设置。

java.util.PriorityQueue#siftUpComparable private void siftUpComparable(int k, E x) { Comparable<? super E> key = (Comparable<? super E>) x; //《1》 while (k > 0) { int parent = (k - 1) >>> 1; //《2》 Object e = queue[parent]; //《3》 if (key.compareTo((E) e) >= 0) //《4》 break; queue[k] = e; //《5》 k = parent; } queue[k] = key; //《6》 }

《1》添加的元素必须是Comparable子类,可比较的。

《2》计算父节点下标。

《3》得到父节点元素。

《4》跟父节点元素作比较,如果要添加的元素大于父节点元素则退出。

《5》把父节点的元素移动到数组下标k处,然后把父节点下标赋值给k,循环《1》 - 《4》步骤。

《6》经过前面步骤最终确认需要添加的元素在queue下标,并存入数组。

Queue-PriorityQueue源码解析

添加10 - 8 该方法体现的数据结构。

Queue-PriorityQueue源码解析

添加7整个过程,用堆数据结构添加7的过程只交换了两次数据位置。如果用插叙排序这种极端情况所有数据都需要移动。

最小二叉堆特性是根节点元素值永远是最小的。

PriorityQueue删除元素解析 java.util.PriorityQueue#poll public E poll() { if (size == 0) //《1》 return null; int s = --size; //《2》 modCount++; //《3》 E result = (E) queue[0];//《4》 E x = (E) queue[s];//《5》 queue[s] = null; if (s != 0) siftDown(0, x);//《6》 return result; }

《1》如果队列为空,返回null。

《2》队列元素总数减1。

《3》修改次数加1。

《4》把堆头部元素取出,后面直接返回该元素。

《5》获取queue最后一个元素并把该位置设置null。

《6》重新筛选最小值为头部元素。

java.util.PriorityQueue#siftDown private void siftDown(int k, E x) { if (comparator != null) siftDownUsingComparator(k, x); else siftDownComparable(k, x); }

如果构造PriorityQueue时传有特定比较器,就按特定比较器方式设置顶部元素,否则按默认自然比较器方式设置。

java.util.PriorityQueue#siftDownComparable private void siftDownComparable(int k, E x) { Comparable<? super E> key = (Comparable<? super E>)x; int half = size >>> 1; //《1》 // loop while a non-leaf while (k < half) { int child = (k << 1) + 1; //《2》 // assume left child is least Object c = queue[child];//《3》 int right = child + 1;//《4》 if (right < size && ((Comparable<? super E>) c).compareTo((E) queue[right]) > 0) //《5》 c = queue[child = right]; if (key.compareTo((E) c) <= 0)//《6》 break; queue[k] = c;//《7》 k = child; } queue[k] = key;//《8》 }

《1》无符号右移1位,取size的一半。

《2》得到二叉堆的左子节点下标。

《3》获取左子节点元素。

《4》右子节点下标。

《5》右子节点下标小于队列元素总数,并且左子节点元素比右子节点元素大时,把右子节点元素赋值给c,把右子节点下标赋值给child。

《6》需要交换的元素key小于或等于子节点元素c,则退出循环。

《7》把子节点c设置到queue下标为k的位置,并把child赋值给k,然后重复《1》-《6》步骤。

《8》找到key合适的位置并设置该元素。

总结

PriorityQueue使用二叉堆数据结构保证了队列头部元素永远是最小的,在添加和删除的过程元素移动次数比插叙排序插入少。队列元素是使用数组queue保存,在多线程的情况对数组queue并发操作存在安全问题。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wppdxw.html