View Javadoc
1   /*
2    * Copyright 2015 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty5.util.internal;
17  
18  import java.util.AbstractQueue;
19  import java.util.Arrays;
20  import java.util.Comparator;
21  import java.util.Iterator;
22  import java.util.NoSuchElementException;
23  
24  import static io.netty5.util.internal.PriorityQueueNode.INDEX_NOT_IN_QUEUE;
25  import static java.util.Objects.requireNonNull;
26  
27  /**
28   * A priority queue which uses natural ordering of elements. Elements are also required to be of type
29   * {@link PriorityQueueNode} for the purpose of maintaining the index in the priority queue.
30   * @param <T> The object that is maintained in the queue.
31   */
32  public final class DefaultPriorityQueue<T extends PriorityQueueNode> extends AbstractQueue<T>
33                                                                       implements PriorityQueue<T> {
34      private static final PriorityQueueNode[] EMPTY_ARRAY = new PriorityQueueNode[0];
35      private final Comparator<T> comparator;
36      private T[] queue;
37      private int size;
38  
39      @SuppressWarnings("unchecked")
40      public DefaultPriorityQueue(Comparator<T> comparator, int initialSize) {
41          this.comparator = requireNonNull(comparator, "comparator");
42          queue = (T[]) (initialSize != 0 ? new PriorityQueueNode[initialSize] : EMPTY_ARRAY);
43      }
44  
45      @Override
46      public int size() {
47          return size;
48      }
49  
50      @Override
51      public boolean isEmpty() {
52          return size == 0;
53      }
54  
55      @Override
56      public boolean contains(Object o) {
57          if (!(o instanceof PriorityQueueNode)) {
58              return false;
59          }
60          PriorityQueueNode node = (PriorityQueueNode) o;
61          return contains(node, node.priorityQueueIndex(this));
62      }
63  
64      @Override
65      public boolean containsTyped(T node) {
66          return contains(node, node.priorityQueueIndex(this));
67      }
68  
69      @Override
70      public void clear() {
71          for (int i = 0; i < size; ++i) {
72              T node = queue[i];
73              if (node != null) {
74                  node.priorityQueueIndex(this, INDEX_NOT_IN_QUEUE);
75                  queue[i] = null;
76              }
77          }
78          size = 0;
79      }
80  
81      @Override
82      public void clearIgnoringIndexes() {
83          size = 0;
84      }
85  
86      @Override
87      public boolean offer(T e) {
88          if (e.priorityQueueIndex(this) != INDEX_NOT_IN_QUEUE) {
89              throw new IllegalArgumentException("e.priorityQueueIndex(): " + e.priorityQueueIndex(this) +
90                      " (expected: " + INDEX_NOT_IN_QUEUE + ") + e: " + e);
91          }
92  
93          // Check that the array capacity is enough to hold values by doubling capacity.
94          if (size >= queue.length) {
95              // Use a policy which allows for a 0 initial capacity. Same policy as JDK's priority queue, double when
96              // "small", then grow by 50% when "large".
97              queue = Arrays.copyOf(queue, queue.length + ((queue.length < 64) ?
98                                                           (queue.length + 2) :
99                                                           (queue.length >>> 1)));
100         }
101 
102         bubbleUp(size++, e);
103         return true;
104     }
105 
106     @Override
107     public T poll() {
108         if (size == 0) {
109             return null;
110         }
111         T result = queue[0];
112         result.priorityQueueIndex(this, INDEX_NOT_IN_QUEUE);
113 
114         T last = queue[--size];
115         queue[size] = null;
116         if (size != 0) { // Make sure we don't add the last element back.
117             bubbleDown(0, last);
118         }
119 
120         return result;
121     }
122 
123     @Override
124     public T peek() {
125         return (size == 0) ? null : queue[0];
126     }
127 
128     @SuppressWarnings("unchecked")
129     @Override
130     public boolean remove(Object o) {
131         final T node;
132         try {
133             node = (T) o;
134         } catch (ClassCastException e) {
135             return false;
136         }
137         return removeTyped(node);
138     }
139 
140     @Override
141     public boolean removeTyped(T node) {
142         int i = node.priorityQueueIndex(this);
143         if (!contains(node, i)) {
144             return false;
145         }
146 
147         node.priorityQueueIndex(this, INDEX_NOT_IN_QUEUE);
148         if (--size == 0 || size == i) {
149             // If there are no node left, or this is the last node in the array just remove and return.
150             queue[i] = null;
151             return true;
152         }
153 
154         // Move the last element where node currently lives in the array.
155         T moved = queue[i] = queue[size];
156         queue[size] = null;
157         // priorityQueueIndex will be updated below in bubbleUp or bubbleDown
158 
159         // Make sure the moved node still preserves the min-heap properties.
160         if (comparator.compare(node, moved) < 0) {
161             bubbleDown(i, moved);
162         } else {
163             bubbleUp(i, moved);
164         }
165         return true;
166     }
167 
168     @Override
169     public void priorityChanged(T node) {
170         int i = node.priorityQueueIndex(this);
171         if (!contains(node, i)) {
172             return;
173         }
174 
175         // Preserve the min-heap property by comparing the new priority with parents/children in the heap.
176         if (i == 0) {
177             bubbleDown(i, node);
178         } else {
179             // Get the parent to see if min-heap properties are violated.
180             int iParent = (i - 1) >>> 1;
181             T parent = queue[iParent];
182             if (comparator.compare(node, parent) < 0) {
183                 bubbleUp(i, node);
184             } else {
185                 bubbleDown(i, node);
186             }
187         }
188     }
189 
190     @Override
191     public Object[] toArray() {
192         return Arrays.copyOf(queue, size);
193     }
194 
195     @SuppressWarnings("unchecked")
196     @Override
197     public <X> X[] toArray(X[] a) {
198         if (a.length < size) {
199             return (X[]) Arrays.copyOf(queue, size, a.getClass());
200         }
201         System.arraycopy(queue, 0, a, 0, size);
202         if (a.length > size) {
203             a[size] = null;
204         }
205         return a;
206     }
207 
208     /**
209      * This iterator does not return elements in any particular order.
210      */
211     @Override
212     public Iterator<T> iterator() {
213         return new PriorityQueueIterator();
214     }
215 
216     private final class PriorityQueueIterator implements Iterator<T> {
217         private int index;
218 
219         @Override
220         public boolean hasNext() {
221             return index < size;
222         }
223 
224         @Override
225         public T next() {
226             if (index >= size) {
227                 throw new NoSuchElementException();
228             }
229 
230             return queue[index++];
231         }
232 
233         @Override
234         public void remove() {
235             throw new UnsupportedOperationException("remove");
236         }
237     }
238 
239     private boolean contains(PriorityQueueNode node, int i) {
240         return i >= 0 && i < size && node.equals(queue[i]);
241     }
242 
243     private void bubbleDown(int k, T node) {
244         final int half = size >>> 1;
245         while (k < half) {
246             // Compare node to the children of index k.
247             int iChild = (k << 1) + 1;
248             T child = queue[iChild];
249 
250             // Make sure we get the smallest child to compare against.
251             int rightChild = iChild + 1;
252             if (rightChild < size && comparator.compare(child, queue[rightChild]) > 0) {
253                 child = queue[iChild = rightChild];
254             }
255             // If the bubbleDown node is less than or equal to the smallest child then we will preserve the min-heap
256             // property by inserting the bubbleDown node here.
257             if (comparator.compare(node, child) <= 0) {
258                 break;
259             }
260 
261             // Bubble the child up.
262             queue[k] = child;
263             child.priorityQueueIndex(this, k);
264 
265             // Move down k down the tree for the next iteration.
266             k = iChild;
267         }
268 
269         // We have found where node should live and still satisfy the min-heap property, so put it in the queue.
270         queue[k] = node;
271         node.priorityQueueIndex(this, k);
272     }
273 
274     private void bubbleUp(int k, T node) {
275         while (k > 0) {
276             int iParent = (k - 1) >>> 1;
277             T parent = queue[iParent];
278 
279             // If the bubbleUp node is less than the parent, then we have found a spot to insert and still maintain
280             // min-heap properties.
281             if (comparator.compare(node, parent) >= 0) {
282                 break;
283             }
284 
285             // Bubble the parent down.
286             queue[k] = parent;
287             parent.priorityQueueIndex(this, k);
288 
289             // Move k up the tree for the next iteration.
290             k = iParent;
291         }
292 
293         // We have found where node should live and still satisfy the min-heap property, so put it in the queue.
294         queue[k] = node;
295         node.priorityQueueIndex(this, k);
296     }
297 }