Implementing peek, add, and remove
peek reads the root at index 0, and throws NoSuchElementException if the heap is empty:
@Override
public T peek() {
if (size == 0) {
throw new NoSuchElementException();
}
return heap[0]; // the highest-priority element is always at the root
}
add rejects null, grows the array if it is full, places the new element in the next open slot at the end, and swims it up:
@Override
public void add(T item) {
if (item == null) {
throw new IllegalArgumentException();
}
if (size == heap.length) {
grow();
}
heap[size] = item; // the next open slot
size++;
swim(size - 1);
}
grow is the same doubling helper as ArrayList’s, with a Comparable[] in place of an Object[]: allocate an array of twice the length and copy the elements over. The element goes into index size, and after size++ that index is size - 1, which is the slot swim starts from.
remove overwrites the root with the last element and sinks it down:
@Override
public void remove() {
if (size == 0) {
throw new NoSuchElementException();
}
heap[0] = heap[size - 1]; // move the last element into the root
heap[size - 1] = null; // clear the vacated slot
size--;
if (size > 0) {
sink(0);
}
}
Moving the last element into the root keeps the tree complete, and sink(0) restores the order property. (When the heap holds a single element, the two lines above move it onto itself and then clear it, which removes it correctly.)
Run ArrayHeapTest, which runs the PriorityQueueTest suite against the class, to confirm it satisfies every requirement in the contract.