First, it could be insisted that the compareTo method implemented by the objects in the queue should be consistent with equals. Effectively that would mean that no two distinct items could have the same priority. It would then be the responsibility of the client to tag items with priorities corresponding to the order in which they arrive, and this would form part of the definition of the compareTo method for objects of this type. For example, allocation of priorities to airline standby passengers might be determined by such factors as frequent-flyer status, fare paid, and check-in time. Assuming no two passengers checked in at the same time, no two passengers would ever have the same priority; or if they did check in at the same time, at different check-in desks for example, it should be a matter of indifference, on that basis alone, which had the higher priority.
Alternatively, we might wish to provide the tagging automatically for the client. A priority queue would then be a genuine queue. Amongst items of the same priority, the first in would always be the first out. On that approach, the question of the most efficient implementation would depend largely on whether we expect items of the same priority to be the exception or the rule. If items mainly have the same priority, it would be best to implement a priority queue as a collection of ordinary queues. But these queues would have to be structured in such a way that, when adding to the priority queue, there was an efficient way of finding which sub-queue should be extended.
Rather than pursue that line, we consider a simple implementation which retains a binary heap as the underlying data-structure, but which supplies an internal tag indicating the time of joining the queue. The order relation for the binary heap will then also take account of this time. We call such a binary heap a StrictBinaryHeap and code for its implementation is given in Figure 9.5.
public class StrictBinaryHeap implements PriorityQueue {
private BinaryHeap h;
private static int ticks = 0;
private class Item implements Comparable {
Comparable item;
int time;
Item(Comparable item, int time) {
this.item = item;
this.time = time;
}
public int compareTo(Object other) {
int result = item.compareTo(((Item) other).item);
if (result == 0) {
result = ((Item) other).time - time;
}
return result;
}
}
public StrictBinaryHeap() {
h = new BinaryHeap();
}
public boolean isEmpty() {
return h.isEmpty();
}
public int size() {
return h.size();
}
public void add(Comparable item) {
h.add(new Item(item, ticks++));
}
public Comparable remove() {
return ((Item) h.remove()).item;
}
}
|
This is simpler than it looks. Essentially we make use of a simple BinaryHeap called h, but instead of inserting items immediately into h with their simple priority ordering, we tag each by the time of arrival. This is done using a private class Item which contains not just the original item to be inserted, but also the time of arrival. Members of this class are compared for order by using their original priorities, if they are different, and otherwise by their time of arrival. The clock is advanced by one tick each time a new item is added:
public void add(Comparable item) {
h.add(new Item(item, ticks++));
}
The variable ticks is declared to be static so that
it would be possible to merge two or more priority queues implemented
in this way, using the common clock to compare their orders of
arrival.
On balance, applications may find it preferable to use a simple BinaryHeap, with responsibility being left to the client to ensure that any preferences for order of emergence from the queue are embodied in the original compareTo method of the items to be queued.