jdk有一个java.util.PriorityQueue。lucene中也有个PriorityQueue.为了节约空间和更高效,lucene没有使用jdkp中的PriorityQueue.它们的主要读取方法时间都是的log(n),但也有些不同。
下面看看lucene中使用的PriorityQueue。它内部是一个堆排序。
java.util.PriorityQueue是一个自增长的优先队列.也就是说,随着插入元素的增加,它的容量会越来越大,而 org.apache.lucene.util.PriorityQueue则不会,它是一个指定的容量的优先队列。因为它保存的始终是最大的数据.因此,如果只取前n个数据时可以用lucene的
PriorityQueue,这样可以达到节省内存。
下面分析一下原代码.
下面是lucene中的PriorityQueue的插入节点的实现细节,
public Object insertWithOverflow(Object element) {
if (size < maxSize) {//小于最大容量,直接放进堆
put(element);
return null;
} else if (size > 0 && !lessThan(element, heap[1])) {//如果当前的堆已满,并且大于当前最小的元素,则将最小元素返回,并插入新元素
Object ret = heap[1];
heap[1] = element;
adjustTop();//重新进行堆排序
return ret;
} else {//堆已满并且要插入的元素小于堆中最小的元素
return element;
}
}
heap[1]是堆顶,保存堆中的最小元素.
由引看出在org.apache.lucene.util.PriorityQueue中n是固定的,因此重新进行堆排序的时间是恒定的。
由引看出在org.apache.lucene.util.PriorityQueue中n是固定的,因此重新进行堆排序的时间是恒定的。
可能是为了提高插入效率,在org.apache.lucene.util.PriorityQueue的实现类TopDocCollector类中保存了队列中的最小元素,如果存在最小元素,则可以先与最小元素进行比较.再决定是否执行插入操作.
用变量保存了当前队列中最小的元素.以提高效率
public void collect(int doc, float score) {
if (score > 0.0f) {
totalHits++;
if (reusableSD == null) {
reusableSD = new ScoreDoc(doc, score);
} else if (score >= reusableSD.score) {//如果有最小元素,则与最小元素比较。如果大于最小元素则更新最小元素,并执行插入操作.
// reusableSD holds the last "rejected" entry, so, if
// this new score is not better than that, there's no
// need to try inserting it
reusableSD.doc = doc;
reusableSD.score = score;
} else {//如果小于最小元素,直接返回
return;
}
reusableSD = (ScoreDoc) hq.insertWithOverflow(reusableSD);
}
}
不过个人认为insertWithOverflow方法里已经做了类似判断,而且从效率上来讲没区别,没必要在外面增加最小值判断,这样做反而容易引起逻辑不一致.
不过个人认为insertWithOverflow方法里已经做了类似判断,而且从效率上来讲没区别,没必要在外面增加最小值判断,这样做反而容易引起逻辑不一致.