e8efcd82a8
Motivation: We can just use Objects.requireNonNull(...) as a replacement for ObjectUtil.checkNotNull(....) Modifications: - Use Objects.requireNonNull(...) Result: Less code to maintain.
298 lines
9.0 KiB
Java
298 lines
9.0 KiB
Java
/*
|
|
* 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;
|
|
import static java.util.Objects.requireNonNull;
|
|
|
|
/**
|
|
* 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 = requireNonNull(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.
|
|
*/
|
|
@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);
|
|
}
|
|
}
|