RoundRobinQueue.java

  1. package gov.usgs.earthquake.util;

  2. import java.util.ArrayList;
  3. import java.util.Collection;
  4. import java.util.HashMap;
  5. import java.util.Iterator;
  6. import java.util.LinkedList;
  7. import java.util.List;
  8. import java.util.NoSuchElementException;
  9. import java.util.Queue;

  10. /**
  11.  * An abstract base class for round-robin queueing.
  12.  *
  13.  * Sub classes should implement the {@link #getQueueId(Object)} to control how
  14.  * objects are added to queues.
  15.  *
  16.  * @param <T>
  17.  *            type of object being queued.
  18.  */
  19. public class RoundRobinQueue<T> implements Queue<T> {

  20.     /**
  21.      * Map of queues, keyed by queue id (as determined by
  22.      * {@link #getQueueId(Object)}).
  23.      */
  24.     private final HashMap<String, LinkedList<T>> queueMap = new HashMap<String, LinkedList<T>>();

  25.     /**
  26.      * List of queues, same as in map, in round-robin order.
  27.      */
  28.     private final LinkedList<LinkedList<T>> queueList = new LinkedList<LinkedList<T>>();

  29.     /** Default constructor. */
  30.     public RoundRobinQueue() {
  31.     }

  32.     /**
  33.      * This method determines which queue an object uses.
  34.      *
  35.      * @param object
  36.      *            the object being added.
  37.      * @return id of the queue where object should be added.
  38.      */
  39.     protected String getQueueId(T object) {
  40.         return object.toString();
  41.     }

  42.     /** ===================== BLOCKING QUEUE METHODS ======================= **/

  43.     /**
  44.      * Add an item to the queue.
  45.      *
  46.      * @param e
  47.      *            item to add
  48.      * @return true if added.
  49.      */
  50.     @Override
  51.     public boolean add(T e) {
  52.         // find queue
  53.         String queueId = getQueueId(e);
  54.         LinkedList<T> queue = queueMap.get(queueId);
  55.         if (queue == null) {
  56.             // create queue
  57.             queue = new LinkedList<T>();
  58.             queueMap.put(queueId, queue);
  59.             queueList.add(queue);
  60.         }
  61.         // add to queue
  62.         return queue.add(e);
  63.     }

  64.     /**
  65.      * Add an item to the queue, if possible.
  66.      *
  67.      * @param e
  68.      *            item to add
  69.      * @return true if added, false otherwise.
  70.      */
  71.     @Override
  72.     public boolean offer(T e) {
  73.         try {
  74.             // this could in theory throw an unchecked exception
  75.             return this.add(e);
  76.         } catch (Exception ex) {
  77.             return false;
  78.         }
  79.     }

  80.     /**
  81.      * Retrieves and removes the head of this queue.
  82.      *
  83.      * @return first element in queue.
  84.      * @throws NoSuchElementException
  85.      *             if queue is empty.
  86.      */
  87.     @Override
  88.     public T remove() {
  89.         if (queueList.size() == 0) {
  90.             throw new NoSuchElementException();
  91.         }

  92.         T next = null;
  93.         // find queue
  94.         LinkedList<T> nextQueue = queueList.removeFirst();
  95.         // take first item
  96.         next = nextQueue.removeFirst();
  97.         // reschedule queue
  98.         if (nextQueue.size() == 0) {
  99.             // queue is empty, remove
  100.             String queueId = getQueueId(next);
  101.             queueMap.remove(queueId);
  102.         } else {
  103.             // move to end of round robin list
  104.             queueList.add(nextQueue);
  105.         }
  106.         return next;
  107.     }

  108.     /**
  109.      * Retrieves, but does not remove, the head of this queue. This method
  110.      * differs from the {@link #peek()} method only in that it throws an
  111.      * exception if this queue is empty.
  112.      *
  113.      * @return the head of this queue.
  114.      * @throws NoSuchElementException
  115.      *             if this queue is empty.
  116.      */
  117.     @Override
  118.     public T element() {
  119.         if (queueList.size() == 0) {
  120.             throw new NoSuchElementException();
  121.         }
  122.         LinkedList<T> nextQueue = queueList.getFirst();
  123.         return nextQueue.getFirst();
  124.     }

  125.     /**
  126.      * Retrieves and removes the head of this queue.
  127.      *
  128.      * @return the head of this queue, or null if this queue is empty.
  129.      */
  130.     @Override
  131.     public T poll() {
  132.         try {
  133.             return remove();
  134.         } catch (NoSuchElementException nsee) {
  135.             return null;
  136.         }
  137.     }

  138.     /**
  139.      * Retrieves, but does not remove, the head of this queue, returning null if
  140.      * this queue is empty.
  141.      *
  142.      * @return the head of this queue, or null if this queue is empty.
  143.      */
  144.     @Override
  145.     public T peek() {
  146.         try {
  147.             return element();
  148.         } catch (NoSuchElementException nsee) {
  149.             return null;
  150.         }
  151.     }

  152.     /** ======================= COLLECTION METHODS ========================= **/

  153.     @Override
  154.     public boolean addAll(Collection<? extends T> c) {
  155.         Iterator<? extends T> iter = c.iterator();
  156.         boolean added = false;
  157.         while (iter.hasNext()) {
  158.             added = add(iter.next()) || added;
  159.         }
  160.         return added;
  161.     }

  162.     @Override
  163.     public void clear() {
  164.         queueList.clear();
  165.         queueMap.clear();
  166.     }

  167.     @Override
  168.     public boolean containsAll(Collection<?> c) {
  169.         Iterator<?> iter = c.iterator();
  170.         while (iter.hasNext()) {
  171.             if (!contains(iter.next())) {
  172.                 return false;
  173.             }
  174.         }
  175.         return true;
  176.     }

  177.     @Override
  178.     public boolean isEmpty() {
  179.         return queueList.isEmpty();
  180.     }

  181.     @Override
  182.     public boolean removeAll(Collection<?> c) {
  183.         Iterator<?> iter = c.iterator();
  184.         boolean removed = false;
  185.         while (iter.hasNext()) {
  186.             removed = remove(iter.next()) || removed;
  187.         }
  188.         return removed;
  189.     }

  190.     @Override
  191.     public int size() {
  192.         int size = 0;
  193.         Iterator<LinkedList<T>> iter = queueList.iterator();
  194.         while (iter.hasNext()) {
  195.             size = size + iter.next().size();
  196.         }
  197.         return size;
  198.     }

  199.     @Override
  200.     public boolean contains(Object o) {
  201.         try {
  202.             @SuppressWarnings("unchecked")
  203.             String queueId = getQueueId((T) o);
  204.             LinkedList<T> queue = queueMap.get(queueId);
  205.             return queue.contains(o);
  206.         } catch (Exception e) {
  207.             return false;
  208.         }
  209.     }

  210.     @Override
  211.     public boolean remove(Object o) {
  212.         try {
  213.             @SuppressWarnings("unchecked")
  214.             String queueId = getQueueId((T) o);
  215.             LinkedList<T> queue = queueMap.get(queueId);
  216.             boolean removed = queue.remove(o);
  217.             if (queue.isEmpty()) {
  218.                 queueMap.remove(queueId);
  219.                 queueList.remove(queue);
  220.             }
  221.             return removed;
  222.         } catch (Exception e) {
  223.             return false;
  224.         }
  225.     }

  226.     /** ==================== HORRIBLY INEFFICIENT =========================== */

  227.     /**
  228.      * Deep copy of another RoundRobinQueue.
  229.      *
  230.      * This method is used for semi-destructive iteration methods.
  231.      *
  232.      * NOTE: this assumes {@link #getQueueId(Object)} behaves the same for this
  233.      * and that.
  234.      *
  235.      * @param that a RoundRobinQueue to make a deep copy of
  236.      */
  237.     public RoundRobinQueue(final RoundRobinQueue<T> that) {
  238.         Iterator<LinkedList<T>> iter = that.queueList.iterator();
  239.         while (iter.hasNext()) {
  240.             // copy list
  241.             LinkedList<T> copy = new LinkedList<T>(iter.next());
  242.             // add to round robin list
  243.             this.queueList.add(copy);
  244.             // add to map
  245.             String queueId = that.getQueueId(copy.get(0));
  246.             this.queueMap.put(queueId, copy);
  247.         }
  248.     }

  249.     /**
  250.      * Flatten queue to a list.
  251.      *
  252.      * Creates a copy (see {@link #RoundRobinQueue(RoundRobinQueue)}, then
  253.      * builds list by polling until it is empty.
  254.      *
  255.      * @return list of all items currently in queue.
  256.      */
  257.     public List<T> toList() {
  258.         ArrayList<T> list = new ArrayList<T>(this.size());
  259.         RoundRobinQueue<T> copy = new RoundRobinQueue<T>(this);
  260.         while (true) {
  261.             T next = copy.poll();
  262.             if (next == null) {
  263.                 // done
  264.                 break;
  265.             }
  266.             list.add(next);
  267.         }
  268.         return list;
  269.     }

  270.     @Override
  271.     public Iterator<T> iterator() {
  272.         return this.toList().iterator();
  273.     }

  274.     @Override
  275.     public Object[] toArray() {
  276.         return this.toList().toArray();
  277.     }

  278.     @Override
  279.     public <T2> T2[] toArray(T2[] array) {
  280.         return this.toList().toArray(array);
  281.     }

  282.     @Override
  283.     public boolean retainAll(Collection<?> c) {
  284.         List<T> toremove = this.toList();
  285.         toremove.removeAll(c);
  286.         return this.removeAll(toremove);
  287.     }

  288. }