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}