LUCENE-1935: Generify PriorityQueue

git-svn-id: https://svn.apache.org/repos/asf/lucene/java/trunk@821104 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Uwe Schindler 2009-10-02 17:28:03 +00:00
parent 5e114029b0
commit 698b73e2ae
2 changed files with 98 additions and 108 deletions

View File

@ -25,14 +25,14 @@ package org.apache.lucene.util;
* length <code>maxSize+1</code>, in {@link #initialize}.
*
*/
public abstract class PriorityQueue {
public abstract class PriorityQueue<T> {
private int size;
private int maxSize;
protected Object[] heap;
protected T[] heap;
/** Determines the ordering of objects in this priority queue. Subclasses
must define this one method. */
protected abstract boolean lessThan(Object a, Object b);
protected abstract boolean lessThan(T a, T b);
/**
* This method can be overridden by extending classes to return a sentinel
@ -41,7 +41,7 @@ public abstract class PriorityQueue {
* change the top without attempting to insert any new object.<br>
*
* Those sentinel values should always compare worse than any non-sentinel
* value (i.e., {@link #lessThan(Object, Object)} should always favor the
* value (i.e., {@link #lessThan(T, T)} should always favor the
* non-sentinel values).<br>
*
* By default, this method returns false, which means the queue will not be
@ -53,9 +53,9 @@ public abstract class PriorityQueue {
*
* <pre>
* // extends getSentinelObject() to return a non-null value.
* PriorityQueue pq = new MyQueue(numHits);
* PriorityQueue<MyObject> pq = new MyQueue<MyObject>(numHits);
* // save the 'top' element, which is guaranteed to not be null.
* MyObject pqTop = (MyObject) pq.top();
* MyObject pqTop = pq.top();
* &lt;...&gt;
* // now in order to add a new element, which is 'better' than top (after
* // you've verified it is better), it is as simple as:
@ -73,11 +73,12 @@ public abstract class PriorityQueue {
* @return the sentinel object to use to pre-populate the queue, or null if
* sentinel objects are not supported.
*/
protected Object getSentinelObject() {
protected T getSentinelObject() {
return null;
}
/** Subclass constructors must call this. */
@SuppressWarnings("unchecked")
protected final void initialize(int maxSize) {
size = 0;
int heapSize;
@ -86,11 +87,11 @@ public abstract class PriorityQueue {
heapSize = 2;
else
heapSize = maxSize + 1;
heap = new Object[heapSize];
heap = (T[]) new Object[heapSize]; // T is unbounded type, so this unchecked cast works always
this.maxSize = maxSize;
// If sentinel objects are supported, populate the queue with them
Object sentinel = getSentinelObject();
T sentinel = getSentinelObject();
if (sentinel != null) {
heap[1] = sentinel;
for (int i = 2; i < heap.length; i++) {
@ -105,10 +106,10 @@ public abstract class PriorityQueue {
* more objects than maxSize from initialize a RuntimeException
* (ArrayIndexOutOfBound) is thrown.
*
* @deprecated use {@link #add(Object)} which returns the new top object,
* @deprecated use {@link #add(T)} which returns the new top object,
* saving an additional call to {@link #top()}.
*/
public final void put(Object element) {
public final void put(T element) {
size++;
heap[size] = element;
upHeap();
@ -121,7 +122,7 @@ public abstract class PriorityQueue {
*
* @return the new 'top' element in the queue.
*/
public final Object add(Object element) {
public final T add(T element) {
size++;
heap[size] = element;
upHeap();
@ -134,10 +135,10 @@ public abstract class PriorityQueue {
*
* @param element
* @return true if element is added, false otherwise.
* @deprecated use {@link #insertWithOverflow(Object)} instead, which
* @deprecated use {@link #insertWithOverflow(T)} instead, which
* encourages objects reuse.
*/
public boolean insert(Object element) {
public boolean insert(T element) {
return insertWithOverflow(element) != element;
}
@ -151,12 +152,12 @@ public abstract class PriorityQueue {
* heap and now has been replaced by a larger one, or null
* if the queue wasn't yet full with maxSize elements.
*/
public Object insertWithOverflow(Object element) {
public T insertWithOverflow(T element) {
if (size < maxSize) {
put(element);
return null;
} else if (size > 0 && !lessThan(element, heap[1])) {
Object ret = heap[1];
T ret = heap[1];
heap[1] = element;
adjustTop();
return ret;
@ -166,7 +167,7 @@ public abstract class PriorityQueue {
}
/** Returns the least element of the PriorityQueue in constant time. */
public final Object top() {
public final T top() {
// We don't need to check size here: if maxSize is 0,
// then heap is length 2 array with both entries null.
// If size is 0 then heap[1] is already null.
@ -175,9 +176,9 @@ public abstract class PriorityQueue {
/** Removes and returns the least element of the PriorityQueue in log(size)
time. */
public final Object pop() {
public final T pop() {
if (size > 0) {
Object result = heap[1]; // save first value
T result = heap[1]; // save first value
heap[1] = heap[size]; // move last to first
heap[size] = null; // permit GC of objects
size--;
@ -230,7 +231,7 @@ public abstract class PriorityQueue {
*
* @return the new 'top' element.
*/
public final Object updateTop() {
public final T updateTop() {
downHeap();
return heap[1];
}
@ -250,7 +251,7 @@ public abstract class PriorityQueue {
private final void upHeap() {
int i = size;
Object node = heap[i]; // save bottom node
T node = heap[i]; // save bottom node
int j = i >>> 1;
while (j > 0 && lessThan(node, heap[j])) {
heap[i] = heap[j]; // shift parents down
@ -262,7 +263,7 @@ public abstract class PriorityQueue {
private final void downHeap() {
int i = 1;
Object node = heap[i]; // save top node
T node = heap[i]; // save top node
int j = i << 1; // find smaller child
int k = j + 1;
if (k <= size && lessThan(heap[k], heap[j])) {

View File

@ -19,111 +19,100 @@ package org.apache.lucene.util;
import java.util.Random;
public class TestPriorityQueue
extends LuceneTestCase
{
public TestPriorityQueue(String name)
{
super(name);
public class TestPriorityQueue extends LuceneTestCase {
public TestPriorityQueue(String name) {
super(name);
}
private static class IntegerQueue
extends PriorityQueue
{
public IntegerQueue(int count)
{
super();
initialize(count);
}
private static class IntegerQueue extends PriorityQueue<Integer> {
public IntegerQueue(int count) {
super();
initialize(count);
}
protected boolean lessThan(Object a, Object b)
{
return ((Integer) a).intValue() < ((Integer) b).intValue();
}
protected boolean lessThan(Integer a, Integer b) {
return (a < b);
}
}
public void testPQ()
throws Exception
{
testPQ(10000, newRandom());
public void testPQ() throws Exception {
testPQ(10000, newRandom());
}
public static void testPQ(int count, Random gen)
{
PriorityQueue pq = new IntegerQueue(count);
int sum = 0, sum2 = 0;
public static void testPQ(int count, Random gen) {
PriorityQueue<Integer> pq = new IntegerQueue(count);
int sum = 0, sum2 = 0;
for (int i = 0; i < count; i++)
{
int next = gen.nextInt();
sum += next;
pq.put(new Integer(next));
}
for (int i = 0; i < count; i++)
{
int next = gen.nextInt();
sum += next;
pq.put(next);
}
// Date end = new Date();
// Date end = new Date();
// System.out.print(((float)(end.getTime()-start.getTime()) / count) * 1000);
// System.out.println(" microseconds/put");
// System.out.print(((float)(end.getTime()-start.getTime()) / count) * 1000);
// System.out.println(" microseconds/put");
// start = new Date();
// start = new Date();
int last = Integer.MIN_VALUE;
for (int i = 0; i < count; i++)
{
Integer next = (Integer)pq.pop();
assertTrue(next.intValue() >= last);
last = next.intValue();
sum2 += last;
}
int last = Integer.MIN_VALUE;
for (int i = 0; i < count; i++)
{
Integer next = (Integer)pq.pop();
assertTrue(next.intValue() >= last);
last = next.intValue();
sum2 += last;
}
assertEquals(sum, sum2);
// end = new Date();
assertEquals(sum, sum2);
// end = new Date();
// System.out.print(((float)(end.getTime()-start.getTime()) / count) * 1000);
// System.out.println(" microseconds/pop");
// System.out.print(((float)(end.getTime()-start.getTime()) / count) * 1000);
// System.out.println(" microseconds/pop");
}
public void testClear()
{
PriorityQueue pq = new IntegerQueue(3);
pq.put(new Integer(2));
pq.put(new Integer(3));
pq.put(new Integer(1));
assertEquals(3, pq.size());
pq.clear();
assertEquals(0, pq.size());
}
public void testFixedSize(){
PriorityQueue pq = new IntegerQueue(3);
pq.insert(new Integer(2));
pq.insert(new Integer(3));
pq.insert(new Integer(1));
pq.insert(new Integer(5));
pq.insert(new Integer(7));
pq.insert(new Integer(1));
public void testClear() {
PriorityQueue<Integer> pq = new IntegerQueue(3);
pq.put(2);
pq.put(3);
pq.put(1);
assertEquals(3, pq.size());
assertEquals(3, ((Integer) pq.top()).intValue());
pq.clear();
assertEquals(0, pq.size());
}
public void testInsertWithOverflow() {
int size = 4;
PriorityQueue pq = new IntegerQueue(size);
Integer i1 = new Integer(2);
Integer i2 = new Integer(3);
Integer i3 = new Integer(1);
Integer i4 = new Integer(5);
Integer i5 = new Integer(7);
Integer i6 = new Integer(1);
public void testFixedSize() {
PriorityQueue<Integer> pq = new IntegerQueue(3);
pq.insert(2);
pq.insert(3);
pq.insert(1);
pq.insert(5);
pq.insert(7);
pq.insert(1);
assertEquals(3, pq.size());
assertEquals((Integer) 3, pq.top());
}
assertNull(pq.insertWithOverflow(i1));
assertNull(pq.insertWithOverflow(i2));
assertNull(pq.insertWithOverflow(i3));
assertNull(pq.insertWithOverflow(i4));
assertTrue(pq.insertWithOverflow(i5) == i3); // i3 should have been dropped
assertTrue(pq.insertWithOverflow(i6) == i6); // i6 should not have been inserted
assertEquals(size, pq.size());
assertEquals(2, ((Integer) pq.top()).intValue());
}
public void testInsertWithOverflow() {
int size = 4;
PriorityQueue<Integer> pq = new IntegerQueue(size);
Integer i1 = 2;
Integer i2 = 3;
Integer i3 = 1;
Integer i4 = 5;
Integer i5 = 7;
Integer i6 = 1;
assertNull(pq.insertWithOverflow(i1));
assertNull(pq.insertWithOverflow(i2));
assertNull(pq.insertWithOverflow(i3));
assertNull(pq.insertWithOverflow(i4));
assertTrue(pq.insertWithOverflow(i5) == i3); // i3 should have been dropped
assertTrue(pq.insertWithOverflow(i6) == i6); // i6 should not have been inserted
assertEquals(size, pq.size());
assertEquals((Integer) 2, pq.top());
}
}