1 jdk PriorityQueue简述
上文讲述了netty优先级队列的实现方式,本文将jdk1.8源码实现的优先级队列,做进一步比较和分析.
从下图继承关系可知,netty实现与jdk实现优先级队列原理类似,只是netty针对task做了更多特定的封装,而dk实现的PriorityQueue通用性更强;
2 jdk PriorityQueue基本组成
与netty实现类似,这里也是以数组实现堆结构来存储优先级队列!同样也存在比较器,方便用户灵活定义比较功能;
3 入队操作-offer分析
jdk实现的入队操作比netty实现更具有通用性,假如内置比较器为null,则会使用节点,强制转换为Comparable类型,使用该类型进行比较,其他具体操作除了部分细节不同外,基本上和netty的优先级队列实现差别不大!
//offer实现
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)
queue[0] = e;
else
siftUp(i, e);
return true;
}
//siftUp实现
private void siftUp(int k, E x) {
if (comparator != null)
siftUpUsingComparator(k, x);
else
siftUpComparable(k, x);
}
//从下往上堆化
@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;
}
4 出队操作-push分析
出队操作原理与netty实现基本类似,也是先取堆顶元素,然后取最后一个节点,自顶而下与子节点进行比较,进行堆化操作!
//poll实现
@SuppressWarnings("unchecked")
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;
}
//siftDown实现
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;
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;
}
5 本文小结
jdk实现的优先级队列本质与netty实现基本一致,均是使用数组构建小顶堆,插入则从下网上比较堆化,删除则取最后一个元素从上往下进行堆化处理!
总结一下,这里有如下几点值得学习的:
- 感受netty与jdk的优先级队列实现思路:基本思路一致,netty定制了scheculeTask的一些特殊行为,而jdk则在通用性下了更多的功夫;
- jdk实现中同样存在扩容问题,这里思路也与netty实现吻合;
- 为了保证容错性,当比较器为空时候,jdk实现会使用比较器接口强制转换队列的存储对象,这里的选择值得商榷,必须保证如果比较器未初始化,存储节点必须继承实现可比较接口!