/*
 * Copyright 2015 The Netty Project
 *
 * The Netty Project licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
package io.netty.util.internal;

import java.util.AbstractQueue;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Iterator;
import java.util.NoSuchElementException;

import static io.netty.util.internal.PriorityQueueNode.INDEX_NOT_IN_QUEUE;

A priority queue which uses natural ordering of elements. Elements are also required to be of type PriorityQueueNode for the purpose of maintaining the index in the priority queue.
Type parameters:
  • <T> – The object that is maintained in the queue.
/** * A priority queue which uses natural ordering of elements. Elements are also required to be of type * {@link PriorityQueueNode} for the purpose of maintaining the index in the priority queue. * @param <T> The object that is maintained in the queue. */
public final class DefaultPriorityQueue<T extends PriorityQueueNode> extends AbstractQueue<T> implements PriorityQueue<T> { private static final PriorityQueueNode[] EMPTY_ARRAY = new PriorityQueueNode[0]; private final Comparator<T> comparator; private T[] queue; private int size; @SuppressWarnings("unchecked") public DefaultPriorityQueue(Comparator<T> comparator, int initialSize) { this.comparator = ObjectUtil.checkNotNull(comparator, "comparator"); queue = (T[]) (initialSize != 0 ? new PriorityQueueNode[initialSize] : EMPTY_ARRAY); } @Override public int size() { return size; } @Override public boolean isEmpty() { return size == 0; } @Override public boolean contains(Object o) { if (!(o instanceof PriorityQueueNode)) { return false; } PriorityQueueNode node = (PriorityQueueNode) o; return contains(node, node.priorityQueueIndex(this)); } @Override public boolean containsTyped(T node) { return contains(node, node.priorityQueueIndex(this)); } @Override public void clear() { for (int i = 0; i < size; ++i) { T node = queue[i]; if (node != null) { node.priorityQueueIndex(this, INDEX_NOT_IN_QUEUE); queue[i] = null; } } size = 0; } @Override public void clearIgnoringIndexes() { size = 0; } @Override public boolean offer(T e) { if (e.priorityQueueIndex(this) != INDEX_NOT_IN_QUEUE) { throw new IllegalArgumentException("e.priorityQueueIndex(): " + e.priorityQueueIndex(this) + " (expected: " + INDEX_NOT_IN_QUEUE + ") + e: " + e); } // Check that the array capacity is enough to hold values by doubling capacity. if (size >= queue.length) { // Use a policy which allows for a 0 initial capacity. Same policy as JDK's priority queue, double when // "small", then grow by 50% when "large". queue = Arrays.copyOf(queue, queue.length + ((queue.length < 64) ? (queue.length + 2) : (queue.length >>> 1))); } bubbleUp(size++, e); return true; } @Override public T poll() { if (size == 0) { return null; } T result = queue[0]; result.priorityQueueIndex(this, INDEX_NOT_IN_QUEUE); T last = queue[--size]; queue[size] = null; if (size != 0) { // Make sure we don't add the last element back. bubbleDown(0, last); } return result; } @Override public T peek() { return (size == 0) ? null : queue[0]; } @SuppressWarnings("unchecked") @Override public boolean remove(Object o) { final T node; try { node = (T) o; } catch (ClassCastException e) { return false; } return removeTyped(node); } @Override public boolean removeTyped(T node) { int i = node.priorityQueueIndex(this); if (!contains(node, i)) { return false; } node.priorityQueueIndex(this, INDEX_NOT_IN_QUEUE); if (--size == 0 || size == i) { // If there are no node left, or this is the last node in the array just remove and return. queue[i] = null; return true; } // Move the last element where node currently lives in the array. T moved = queue[i] = queue[size]; queue[size] = null; // priorityQueueIndex will be updated below in bubbleUp or bubbleDown // Make sure the moved node still preserves the min-heap properties. if (comparator.compare(node, moved) < 0) { bubbleDown(i, moved); } else { bubbleUp(i, moved); } return true; } @Override public void priorityChanged(T node) { int i = node.priorityQueueIndex(this); if (!contains(node, i)) { return; } // Preserve the min-heap property by comparing the new priority with parents/children in the heap. if (i == 0) { bubbleDown(i, node); } else { // Get the parent to see if min-heap properties are violated. int iParent = (i - 1) >>> 1; T parent = queue[iParent]; if (comparator.compare(node, parent) < 0) { bubbleUp(i, node); } else { bubbleDown(i, node); } } } @Override public Object[] toArray() { return Arrays.copyOf(queue, size); } @SuppressWarnings("unchecked") @Override public <X> X[] toArray(X[] a) { if (a.length < size) { return (X[]) Arrays.copyOf(queue, size, a.getClass()); } System.arraycopy(queue, 0, a, 0, size); if (a.length > size) { a[size] = null; } return a; }
This iterator does not return elements in any particular order.
/** * This iterator does not return elements in any particular order. */
@Override public Iterator<T> iterator() { return new PriorityQueueIterator(); } private final class PriorityQueueIterator implements Iterator<T> { private int index; @Override public boolean hasNext() { return index < size; } @Override public T next() { if (index >= size) { throw new NoSuchElementException(); } return queue[index++]; } @Override public void remove() { throw new UnsupportedOperationException("remove"); } } private boolean contains(PriorityQueueNode node, int i) { return i >= 0 && i < size && node.equals(queue[i]); } private void bubbleDown(int k, T node) { final int half = size >>> 1; while (k < half) { // Compare node to the children of index k. int iChild = (k << 1) + 1; T child = queue[iChild]; // Make sure we get the smallest child to compare against. int rightChild = iChild + 1; if (rightChild < size && comparator.compare(child, queue[rightChild]) > 0) { child = queue[iChild = rightChild]; } // If the bubbleDown node is less than or equal to the smallest child then we will preserve the min-heap // property by inserting the bubbleDown node here. if (comparator.compare(node, child) <= 0) { break; } // Bubble the child up. queue[k] = child; child.priorityQueueIndex(this, k); // Move down k down the tree for the next iteration. k = iChild; } // We have found where node should live and still satisfy the min-heap property, so put it in the queue. queue[k] = node; node.priorityQueueIndex(this, k); } private void bubbleUp(int k, T node) { while (k > 0) { int iParent = (k - 1) >>> 1; T parent = queue[iParent]; // If the bubbleUp node is less than the parent, then we have found a spot to insert and still maintain // min-heap properties. if (comparator.compare(node, parent) >= 0) { break; } // Bubble the parent down. queue[k] = parent; parent.priorityQueueIndex(this, k); // Move k up the tree for the next iteration. k = iParent; } // We have found where node should live and still satisfy the min-heap property, so put it in the queue. queue[k] = node; node.priorityQueueIndex(this, k); } }