001/** 002 * Copyright (c) 2011, The University of Southampton and the individual contributors. 003 * All rights reserved. 004 * 005 * Redistribution and use in source and binary forms, with or without modification, 006 * are permitted provided that the following conditions are met: 007 * 008 * * Redistributions of source code must retain the above copyright notice, 009 * this list of conditions and the following disclaimer. 010 * 011 * * Redistributions in binary form must reproduce the above copyright notice, 012 * this list of conditions and the following disclaimer in the documentation 013 * and/or other materials provided with the distribution. 014 * 015 * * Neither the name of the University of Southampton nor the names of its 016 * contributors may be used to endorse or promote products derived from this 017 * software without specific prior written permission. 018 * 019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030package org.openimaj.util.queue; 031 032import java.util.ArrayList; 033import java.util.Arrays; 034import java.util.Collections; 035import java.util.Comparator; 036import java.util.List; 037import java.util.PriorityQueue; 038 039/** 040 * A bounded priority queue based on an {@link InvertedPriorityQueue}. 041 * Insertions to the queue are worst-case O(log(N)) and O(1) if the insertion is 042 * rejected. {@link #peek()} and {@link #poll()} are very inefficient (O(N)) as 043 * they have to search for the head of the queue. {@link #peekTail()} and 044 * {@link #pollTail()} have complexity O(1) and O(log(N)) respectively. 045 * <p> 046 * This implementation is ideally suited for storing the top-N items of a 047 * process; that is, where items are constantly added, but not removed very 048 * often. 049 * <p> 050 * The class contains a number of utility methods to get a sorted copy of the 051 * queue. 052 * <p> 053 * The Iterator provided in method {@link #iterator()} is <em>not</em> 054 * guaranteed to traverse the elements of the priority queue in any particular 055 * order. 056 * 057 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 058 * 059 * @param <T> 060 */ 061public class BoundedPriorityQueue<T> extends InvertedPriorityQueue<T> { 062 private static final long serialVersionUID = 1L; 063 private int maxSize; 064 065 /** 066 * Creates a {@code BoundedPriorityQueue} with the specified initial 067 * capacity that orders its elements according to the inverse of the 068 * specified comparator. 069 * 070 * @param maxSize 071 * the maximum number of elements in this priority queue 072 * @param comparator 073 * the comparator that will be used to order this priority queue. 074 * If {@code null}, the {@linkplain Comparable natural ordering} 075 * of the elements will be used. Internally, the comparator is 076 * inverted to reverse its meaning. 077 * @throws IllegalArgumentException 078 * if {@code initialCapacity} is less than 1 079 */ 080 public BoundedPriorityQueue(int maxSize, Comparator<? super T> comparator) { 081 super(maxSize, comparator); 082 this.maxSize = maxSize; 083 } 084 085 /** 086 * Creates a {@code BoundedPriorityQueue} with the specified initial 087 * capacity that orders its elements according to their inverse 088 * {@linkplain Comparable natural ordering}. 089 * 090 * @param maxSize 091 * the maximum number of elements in this priority queue 092 * @throws IllegalArgumentException 093 * if {@code initialCapacity} is less than 1 094 */ 095 public BoundedPriorityQueue(int maxSize) { 096 super(maxSize); 097 this.maxSize = maxSize; 098 } 099 100 /* 101 * (non-Javadoc) 102 * 103 * @see java.util.PriorityQueue#offer(java.lang.Object) 104 */ 105 @Override 106 public boolean offer(T e) { 107 if (this.size() < maxSize) 108 return super.offer(e); 109 else { 110 final T object = super.peek(); 111 112 if (compare(object, e) < 0) { 113 super.poll(); 114 return super.offer(e); 115 } 116 } 117 return false; 118 } 119 120 private int compare(T o1, T o2) { 121 if (this.comparator() != null) { 122 return this.comparator().compare(o1, o2); 123 } else { 124 @SuppressWarnings("unchecked") 125 final Comparable<? super T> key = (Comparable<? super T>) o1; 126 return key.compareTo(o2); 127 } 128 } 129 130 /** 131 * Peak at the head of the queue. To maintain the semantics of a 132 * {@link PriorityQueue}, this is the <emph>least</emph> element with respect to 133 * the specified ordering. 134 * <p> 135 * Peeking at the head is an expensive (O(n)) operation as the 136 * {@link BoundedPriorityQueue} only maintains the tail for fast insertions. 137 * 138 * @see java.util.PriorityQueue#peek() 139 */ 140 @Override 141 public T peek() { 142 T best = super.peek(); 143 144 for (final T obj : this) { 145 if (compare(best, obj) < 0) { 146 best = obj; 147 } 148 } 149 150 return best; 151 } 152 153 /** 154 * Poll the head of the queue. To maintain the semantics of a 155 * {@link PriorityQueue}, this is the <emph>least</emph> element with respect to 156 * the specified ordering. 157 * <p> 158 * Polling the head is an expensive (O(n)) operation as the 159 * {@link BoundedPriorityQueue} only maintains the tail for fast insertions. 160 * 161 * @see java.util.PriorityQueue#poll() 162 */ 163 @Override 164 public T poll() { 165 final T best = peek(); 166 remove(best); 167 return best; 168 } 169 170 /** 171 * Create a new list with the contents of the queue and sort them into their 172 * natural order, or the order specified by the {@link Comparator} used in 173 * constructing the queue. The list constructed in O(N) time, and the 174 * sorting takes O(log(N)) time. 175 * 176 * @return a sorted list containing contents of the queue. 177 */ 178 public List<T> toOrderedList() { 179 final int size = size(); 180 181 final List<T> list = new ArrayList<T>(size); 182 183 for (final T obj : this) { 184 list.add(obj); 185 } 186 187 Collections.sort(list, originalComparator()); 188 189 return list; 190 } 191 192 /** 193 * Returns an array containing all of the elements in this queue. The 194 * elements are in sorted into their natural order, or the order specified 195 * by the {@link Comparator} used in constructing the queue. The array is 196 * constructed in O(N) time, and the sorting takes O(log(N)) time. 197 * <p> 198 * The returned array will be "safe" in that no references to it are 199 * maintained by this queue. (In other words, this method must allocate a 200 * new array). The caller is thus free to modify the returned array. 201 * <p> 202 * This method acts as bridge between array-based and collection-based APIs. 203 * 204 * @return a sorted array containing all of the elements in this queue 205 */ 206 @SuppressWarnings("unchecked") 207 public Object[] toOrderedArray() { 208 final Object[] array = this.toArray(); 209 210 Arrays.sort(array, (Comparator<Object>) originalComparator()); 211 212 return array; 213 } 214 215 /** 216 * Returns a sorted array containing all of the elements in this queue; the 217 * runtime type of the returned array is that of the specified array. 218 * <p> 219 * The elements are in sorted into their natural order, or the order 220 * specified by the {@link Comparator} used in constructing the queue. The 221 * array is constructed in O(N) time, and the sorting takes O(log(N)) time. 222 * <p> 223 * If the queue fits in the specified array, it is returned therein. 224 * Otherwise, a new array is allocated with the runtime type of the 225 * specified array and the size of this queue. 226 * <p> 227 * If the queue fits in the specified array with room to spare (i.e., the 228 * array has more elements than the queue), the element in the array 229 * immediately following the end of the collection is set to {@code null}. 230 * <p> 231 * Like the {@link #toArray()} method, this method acts as bridge between 232 * array-based and collection-based APIs. Further, this method allows 233 * precise control over the runtime type of the output array, and may, under 234 * certain circumstances, be used to save allocation costs. 235 * <p> 236 * Suppose <tt>x</tt> is a queue known to contain only strings. The 237 * following code can be used to dump the queue into a newly allocated array 238 * of <tt>String</tt>: 239 * 240 * <pre> 241 * String[] y = x.toArray(new String[0]); 242 * </pre> 243 * <p> 244 * Note that <tt>toArray(new Object[0])</tt> is identical in function to 245 * <tt>toArray()</tt>. 246 * 247 * @param a 248 * the array into which the elements of the queue are to be 249 * stored, if it is big enough; otherwise, a new array of the 250 * same runtime type is allocated for this purpose. 251 * @return an array containing all of the elements in this queue 252 * @throws ArrayStoreException 253 * if the runtime type of the specified array is not a supertype 254 * of the runtime type of every element in this queue 255 * @throws NullPointerException 256 * if the specified array is null 257 */ 258 public T[] toOrderedArray(T[] a) { 259 final T[] array = this.toArray(a); 260 261 Arrays.sort(array, originalComparator()); 262 263 return array; 264 } 265 266 /** 267 * Create a new list with the contents of the queue with the elements 268 * inserted in their natural order, or the order specified by the 269 * {@link Comparator} used in constructing the queue. 270 * <p> 271 * This method destroys the queue; after the operation completes, the queue 272 * will be empty. The operation completes in O(Nlog(N)) time. 273 * 274 * @return a sorted list containing contents of the queue. 275 */ 276 @SuppressWarnings("unchecked") 277 public List<T> toOrderedListDestructive() { 278 final int size = size(); 279 280 final Object[] list = new Object[size]; 281 282 for (int i = size - 1; i >= 0; i--) { 283 list[i] = super.poll(); 284 } 285 286 return (List<T>) Arrays.asList(list); 287 } 288 289 /** 290 * Returns an array containing all of the elements in this queue. The 291 * elements are in sorted into their natural order, or the order specified 292 * by the {@link Comparator} used in constructing the queue. 293 * <p> 294 * This method destroys the queue; after the operation completes, the queue 295 * will be empty. The operation completes in O(Nlog(N)) time. 296 * <p> 297 * The returned array will be "safe" in that no references to it are 298 * maintained by this queue. (In other words, this method must allocate a 299 * new array). The caller is thus free to modify the returned array. 300 * <p> 301 * This method acts as bridge between array-based and collection-based APIs. 302 * 303 * @return a sorted array containing all of the elements in this queue 304 */ 305 public Object[] toOrderedArrayDestructive() { 306 final int size = size(); 307 308 final Object[] array = new Object[size]; 309 310 for (int i = size - 1; i >= 0; i--) { 311 array[i] = super.poll(); 312 } 313 314 return array; 315 } 316 317 /** 318 * Returns a sorted array containing all of the elements in this queue; the 319 * runtime type of the returned array is that of the specified array. 320 * <p> 321 * The elements are in sorted into their natural order, or the order 322 * specified by the {@link Comparator} used in constructing the queue. 323 * <p> 324 * This method destroys the queue; after the operation completes, the queue 325 * will be empty. The operation completes in O(Nlog(N)) time. 326 * <p> 327 * If the queue fits in the specified array, it is returned therein. 328 * Otherwise, a new array is allocated with the runtime type of the 329 * specified array and the size of this queue. 330 * <p> 331 * If the queue fits in the specified array with room to spare (i.e., the 332 * array has more elements than the queue), the element in the array 333 * immediately following the end of the collection is set to {@code null}. 334 * <p> 335 * Like the {@link #toArray()} method, this method acts as bridge between 336 * array-based and collection-based APIs. Further, this method allows 337 * precise control over the runtime type of the output array, and may, under 338 * certain circumstances, be used to save allocation costs. 339 * <p> 340 * Suppose <tt>x</tt> is a queue known to contain only strings. The 341 * following code can be used to dump the queue into a newly allocated array 342 * of <tt>String</tt>: 343 * 344 * <pre> 345 * String[] y = x.toArray(new String[0]); 346 * </pre> 347 * <p> 348 * Note that <tt>toArray(new Object[0])</tt> is identical in function to 349 * <tt>toArray()</tt>. 350 * 351 * @param a 352 * the array into which the elements of the queue are to be 353 * stored, if it is big enough; otherwise, a new array of the 354 * same runtime type is allocated for this purpose. 355 * @return an array containing all of the elements in this queue 356 * @throws ArrayStoreException 357 * if the runtime type of the specified array is not a supertype 358 * of the runtime type of every element in this queue 359 * @throws NullPointerException 360 * if the specified array is null 361 */ 362 @SuppressWarnings("unchecked") 363 public T[] toOrderedArrayDestructive(T[] a) { 364 final int size = size(); 365 366 if (a.length < size) 367 a = (T[]) Arrays.copyOf(a, size, a.getClass()); 368 369 if (a.length > size) 370 a[size] = null; 371 372 for (int i = size - 1; i >= 0; i--) { 373 a[i] = super.poll(); 374 } 375 376 return a; 377 } 378 379 /** 380 * Retrieves, but does not remove, the tail of this queue, or returns 381 * <tt>null</tt> if this queue is empty. 382 * <p> 383 * This operation is performed in O(1) time. 384 * 385 * @return the tail of this queue, or <tt>null</tt> if this queue is empty. 386 */ 387 public T peekTail() { 388 return super.peek(); 389 } 390 391 /** 392 * Retrieves and remove the tail of this queue, or returns <tt>null</tt> if 393 * this queue is empty. 394 * <p> 395 * This operation is performed in O(1) time. 396 * 397 * @return the tail of this queue, or <tt>null</tt> if this queue is empty. 398 */ 399 public T pollTail() { 400 return super.poll(); 401 } 402 403 /** 404 * Inserts the specified element into this priority queue if possible. This 405 * method is the same as calling {@link #add(Object)} or 406 * {@link #offer(Object)}, but returns a different value depending on the 407 * queue size and whether the item was successfully added. 408 * <p> 409 * Specifically, if the item was not added, then it is returned. If the item 410 * was added to a queue that has not reached its capacity, then 411 * <code>null</code> is returned. If the item was added to a queue at 412 * capacity, then the item that was removed to make space is returned. 413 * 414 * @param item 415 * The item to attempt to add. 416 * 417 * @return The item if it couldn't be added to the queue; null if the item 418 * was added without needing to remove anything; or the removed item 419 * if space had to be made to add the item. 420 * @throws ClassCastException 421 * if the specified element cannot be compared with elements 422 * currently in this priority queue according to the priority 423 * queue's ordering 424 * @throws NullPointerException 425 * if the specified element is null 426 */ 427 public T offerItem(T item) { 428 T tail = null; 429 430 // tail will have to be removed if item is to be added 431 if (this.size() >= maxSize) 432 tail = super.peek(); 433 434 if (this.offer(item)) { 435 return tail; 436 } 437 438 // item was not added 439 return item; 440 } 441 442 /** 443 * Is the {@link BoundedPriorityQueue} full? 444 * 445 * @return true if full; false otherwise 446 */ 447 public boolean isFull() { 448 return this.maxSize == this.size(); 449 } 450}