introduce
A priority queue is a collection of 0 or more elements in which each element has a weight value and each time it leaves the queue, the element with the highest or lowest priority pops up.
Generally speaking, priority queues are implemented using heaps.
Source Code Analysis
Main attributes
// Default capacity private static final int DEFAULT_INITIAL_CAPACITY = 11; // Where elements are stored transient Object[] queue; // non-private to simplify nested class access // Number of elements private int size = 0; // comparator private final Comparator<? super E> comparator; // Number of Modifications transient int modCount = 0; // non-private to simplify nested class access
- The default capacity is 11;
- queue, elements are stored in arrays, which is consistent with what we said earlier about heaps that generally use arrays for storage;
- A comparator, a comparator, also compares elements in a priority queue in two ways: one is the natural order of the elements, the other is the comparison through the comparator;
- modCount, number of modifications, this property indicates that PriorityQueue is also fast-fail;
Entry
There are two ways to enlist, add (E) and offer (E), which are identical, and add (E) is also the offer (E) that is called.
public boolean add(E e) { return offer(e); } public boolean offer(E e) { // null element is not supported if (e == null) throw new NullPointerException(); modCount++; // Take size int i = size; // Number of elements reached maximum capacity, expanding capacity if (i >= queue.length) grow(i + 1); // Number of elements plus 1 size = i + 1; // If there are no elements yet // Insert directly into the first position of the array // This is different from what we talked about before // java starts with zero inside // The heap we're talking about starts with one if (i == 0) queue[0] = e; else // Otherwise, insert the element to the position of the array size, which is next to the last element // Notice that the size here is not the array size, but the number of elements // Then, do a bottom-up heap siftUp(i, e); return true; } private void siftUp(int k, E x) { // Use different methods depending on whether there is a comparator if (comparator != null) siftUpUsingComparator(k, x); else siftUpComparable(k, x); } @SuppressWarnings("unchecked") private void siftUpComparable(int k, E x) { Comparable<? super E> key = (Comparable<? super E>) x; while (k > 0) { // Location of parent node found // Because elements start at 0, subtract 1 and divide by 2 int parent = (k - 1) >>> 1; // Value of parent node Object e = queue[parent]; // Compare values of inserted elements to parent nodes // Jump out of loop if larger than parent node // Otherwise swap locations if (key.compareTo((E) e) >= 0) break; // Swap location with parent node queue[k] = e; // The inserted element position is now moved to the parent node position // Continue comparing with parent node k = parent; } // Find the place where you should insert the element queue[k] = key; }
- null elements are not allowed in the queue;
- If the array is not enough, expand it first;
- If there are no elements, insert the position of subscript 0;
- If there are any elements, insert them into a position behind the last element (there is no actual insertion of a hash);
- Heap from bottom to top, always comparing with the parent node;
- If it is smaller than the parent node, the position is swapped with the parent until it is larger than the parent node.
- Thus, PriorityQueue is a small top heap.
Expansion
private void grow(int minCapacity) { // Old capacity int oldCapacity = queue.length; // Double size if small; else grow by 50% // Double capacity when old capacity is less than 64 // Old capacity greater than or equal to 64, capacity increases by only half of old capacity int newCapacity = oldCapacity + ((oldCapacity < 64) ? (oldCapacity + 2) : (oldCapacity >> 1)); // overflow-conscious code // Check for overflow if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // Create a new array of new capacity size and copy the old array elements to the past queue = Arrays.copyOf(queue, newCapacity); } private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
- Each expansion capacity doubles when the array is smaller (less than 64);
- When the array is large, the capacity of each expansion is only increased by half.
Queue
There are two ways to queue, remove() and poll(), which are also called poll(), but throw an exception when there are no elements.
public E remove() { // Call poll to pop up the first element E x = poll(); if (x != null) // Return pop-up elements when there are elements return x; else // Throw an exception without an element throw new NoSuchElementException(); } @SuppressWarnings("unchecked") public E poll() { // If size is 0, there are no elements if (size == 0) return null; // Pop-up element, number of elements minus 1 int s = --size; modCount++; // Queue head element E result = (E) queue[0]; // End Queue Element E x = (E) queue[s]; // Delete the last element of the queue queue[s] = null; // If there are elements after pop-up if (s != 0) // Move the last element of the queue to the top of the queue // Do top-down heaping again siftDown(0, x); // Return pop-up elements return result; } private void siftDown(int k, E x) { // Choose a different method depending on whether there is a comparator if (comparator != null) siftDownUsingComparator(k, x); else siftDownComparable(k, x); } @SuppressWarnings("unchecked") private void siftDownComparable(int k, E x) { Comparable<? super E> key = (Comparable<? super E>)x; // You only need to compare half because leaf nodes account for half of the elements int half = size >>> 1; // loop while a non-leaf while (k < half) { // Find the location of the child node, where 1 is added because the element starts at position 0 int child = (k << 1) + 1; // assume left child is least // Value of left child node Object c = queue[child]; // Location of the right child node int right = child + 1; if (right < size && ((Comparable<? super E>) c).compareTo((E) queue[right]) > 0) // Left and right nodes are smaller c = queue[child = right]; // End if smaller than child nodes if (key.compareTo((E) c) <= 0) break; // Swap locations if larger than the smallest child node queue[k] = c; // Pointer moves to the smallest child node and continues to compare k = child; } // Find the right place to put elements queue[k] = key; }
- Eject the first element of the queue;
- Move the last element of the queue to the top of the queue;
- Heap up and down, comparing down to the smallest child node;
- If it is larger than the smallest child node, the position is swapped and the comparison with the smallest child node continues.
- If it is smaller than the smallest child node, the heap ends without swapping locations.
- This is the deletion of the top element in the heap;
Take the first element of the queue
There are two ways to get the first element, element() and peek(), which are also called peek(), except that an exception is thrown when the element is not fetched.
public E element() { E x = peek(); if (x != null) return x; else throw new NoSuchElementException(); } public E peek() { return (size == 0) ? null : (E) queue[0]; }
- If there are any elements, remove the element of subscript 0;
- If there are no elements, null is returned and element() throws an exception.
summary
- PriorityQueue is a small top heap;
- PriorityQueue is non-thread safe;
- PriorityQueue is not ordered, only the smallest elements are stored on top of the heap;
- Queuing is the implementation of the insert element of the heap.
- Queuing is the implementation of deleted elements of a heap.