/*
* MinimumQueue.java
*
* Copyright (C) 2010 Leo Osvald <leo.osvald@gmail.com>
*
* This file is part of SGLJ.
*
* SGLJ is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* SGLJ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sglj.util.struct;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Comparator;
import java.util.Deque;
import java.util.Iterator;
import java.util.Queue;
/**
* A {@link Queue} which supports querying for the least/greatest
* element in constant time, through the {@link #query()} method. The queue
* to be used for queue operations is specified via constructor.<br>
* This implementation does not support the following operations:
* <ul>
* <li>{@link #remove(Object)}</li>
* <li>{@link #removeAll(Collection)}</li>
* <li>{@link #retainAll(Collection)}</li>
* </ul>
*
* The primary reason why the above operations are not supported is
* to guarantee constant time querying, regardless of the usage, while
* also avoiding unnecessary overhead and checking.
*
* @author Leo Osvald
*
* @param <E>
*/
public class MinimumQueue<E> implements Queue<E>, GlobalQueryable<E>,
Cloneable, Serializable {
private static final long serialVersionUID = -6888414204844941257L;
private final Queue<E> queue;
private final Comparator<? super E> cmp;
private transient final Deque<E> minQueue;
// constructors for comparator-based ordering
/**
* Constructs a new minimum queue which uses the specified
* comparator for element comparison, delegates queue operations
* to the specified queue and uses the specified deque for internal
* operations.
*
* @param queue the underlying queue
* @param minDeque a deque to be used for internal purposes
* @param comparator the comparator to be used
*/
public MinimumQueue(Queue<E> queue, Deque<E> minDeque,
Comparator<E> comparator) {
this.queue = queue;
this.cmp = comparator;
this.minQueue = minDeque;
}
/**
* Constructs a new minimum queue which uses the specified
* comparator for element comparison, delegates queue operations
* to the specified queue and uses a new instance of
* {@link ArrayDeque} of initial capacity
* equal to <tt>initialCapacity</tt> for internal operations.
*
* @param queue the underlying queue
* @param initialCapacity initial capacity of the internal deque
* @param comparator the comparator to be used
*/
public MinimumQueue(Queue<E> queue, int initialCapacity,
Comparator<E> comparator) {
this(queue, new ArrayDeque<E>(initialCapacity), comparator);
}
/**
* Constructs a new minimum queue which uses the specified
* comparator for element comparison and delegates queue operations
* to the specified queue. A new instance of {@link ArrayDeque}
* is used for internal operations.
*
* @param queue the underlying queue
* @param comparator the comparator to be used
*/
public MinimumQueue(Queue<E> queue, Comparator<E> comparator) {
this(queue, new ArrayDeque<E>(), comparator);
}
/**
* Constructs a new minimum queue which uses an instance
* of {@link ArrayDeque} for queue operations and the specified
* comparator for element comparison. A new instance of {@link ArrayDeque}
* is used for internal operations. Both {@link ArrayDeque} have
* the initial capacity equal to <tt>initialCapacity</tt>.
*
* @param initialCapacity initial capacity of the internal deque
* @param comparator the comparator to be used
*/
public MinimumQueue(int initialCapacity, Comparator<E> comparator) {
this(new ArrayDeque<E>(initialCapacity),
new ArrayDeque<E>(initialCapacity),
comparator);
}
/**
* Constructs a new minimum queue which uses an instance
* of {@link ArrayDeque} for queue operations and the specified
* comparator for element comparison. A new instance of {@link ArrayDeque}
* is used for internal operations.
*
* @param comparator the comparator to be used
*/
public MinimumQueue(Comparator<E> comparator) {
this(new ArrayDeque<E>(), new ArrayDeque<E>(), comparator);
}
// constructors for natural ordering
/**
* Constructs a new minimum queue which uses elements' natural ordering
* for element comparison, delegates queue operations
* to the specified queue and uses the specified deque for internal
* operations.
*
* @param queue the underlying queue
* @param minDeque a deque to be used for internal purposes
*/
public MinimumQueue(Queue<E> queue, Deque<E> minDeque) {
this(queue, minDeque, null);
}
/**
* Constructs a new minimum queue which uses elements' natural ordering
* for element comparison, delegates queue operations
* to the specified queue and uses a new instance of
* {@link ArrayDeque} of initial capacity
* equal to <tt>initialCapacity</tt> for internal operations.
*
* @param queue the underlying queue
* @param initialCapacity initial capacity of the internal deque
*/
public MinimumQueue(Queue<E> queue, int initialCapacity) {
this(queue, new ArrayDeque<E>(initialCapacity), null);
}
/**
* Constructs a new minimum queue uses elements' natural ordering
* for element comparison and delegates queue operations
* to the specified queue. A new instance of {@link ArrayDeque}
* is used for internal operations.
*
* @param queue the underlying queue
*/
public MinimumQueue(Queue<E> queue) {
this(queue, new ArrayDeque<E>(), null);
}
/**
* Constructs a new minimum queue which uses an instance
* of {@link ArrayDeque} for queue operations and elements' natural
* ordering for element comparison. A new instance of {@link ArrayDeque}
* is used for internal operations. Both {@link ArrayDeque} have
* the initial capacity equal to <tt>initialCapacity</tt>.
*
* @param initialCapacity initial capacity of the internal deque
*/
public MinimumQueue(int initialCapacity) {
this(new ArrayDeque<E>(initialCapacity),
new ArrayDeque<E>(initialCapacity),
null);
}
/**
* Constructs a new minimum queue which uses an instance
* of {@link ArrayDeque} for queue operations and elements' natural
* ordering for element comparison. A new instance of {@link ArrayDeque}
* is used for internal operations.
*/
public MinimumQueue() {
this(new ArrayDeque<E>(), new ArrayDeque<E>(), null);
}
@Override
public int size() {
return queue.size();
}
@Override
public boolean isEmpty() {
return queue.isEmpty();
}
@Override
public boolean contains(Object o) {
return queue.contains(o);
}
@Override
public Iterator<E> iterator() {
// disable remove() operation of the underlying queue's iterator
return new NoRemoveItr<E>(queue.iterator());
}
@Override
public Object[] toArray() {
return queue.toArray();
}
@Override
public <T> T[] toArray(T[] a) {
return queue.toArray(a);
}
@Override
public final boolean remove(Object o) {
throw new UnsupportedOperationException();
}
@Override
public boolean containsAll(Collection<?> c) {
return queue.containsAll(c);
}
@Override
public boolean addAll(Collection<? extends E> c) {
boolean ret = queue.addAll(c);
int oldSize = size();
if (oldSize + c.size() == size()) {
if (cmp != null)
fixAfterPushAllComparator(c);
else
fixAfterPushAllComparable(c);
return ret;
}
throw new IllegalStateException("Underlying queue must add all");
}
@Override
public boolean removeAll(Collection<?> c) {
throw new UnsupportedOperationException();
}
@Override
public boolean retainAll(Collection<?> c) {
throw new UnsupportedOperationException();
}
@Override
public void clear() {
queue.clear();
minQueue.clear();
}
@SuppressWarnings("unchecked")
@Override
public boolean add(E e) {
if (queue.add(e)) {
if (cmp != null)
fixAfterPushComparator(e);
else
fixAfterPushComparable((Comparable<? super E>) e);
return true;
}
return false;
}
@SuppressWarnings("unchecked")
@Override
public boolean offer(E e) {
if (queue.offer(e)) {
if (cmp != null)
fixAfterPushComparator(e);
else
fixAfterPushComparable((Comparable<? super E>) e);
return true;
}
return false;
}
@SuppressWarnings("unchecked")
@Override
public E remove() {
E ret = queue.remove();
if (ret != null) {
if (cmp != null)
fixAfterPopComparator(ret);
else
fixAfterPopComparable((Comparable<? super E>)ret);
}
return ret;
}
@SuppressWarnings("unchecked")
@Override
public E poll() {
E ret = queue.poll();
if (ret != null) {
if (cmp != null)
fixAfterPopComparator(ret);
else
fixAfterPopComparable((Comparable<? super E>)ret);
}
return ret;
}
@Override
public E element() {
return queue.element();
}
@Override
public E peek() {
return queue.peek();
}
@Override
public int hashCode() {
return queue.hashCode();
}
@Override
public boolean equals(Object obj) {
return queue.equals(obj);
}
@Override
public String toString() {
return queue.toString() + " | " + minQueue.toString();
}
/**
* Returns the least element in the queue according to the
* {@link Comparator} if other than <code>null</code> or
* according to the elemenets' natural ordering if the comparator
* is <code>null</code>.
* @return the least element in the queue
*/
@Override
public E query() {
return minQueue.isEmpty() ? null : minQueue.peek();
}
public Comparator<? super E> comparator() {
return cmp;
}
/**
* Returns <code>null</code>. This is only used when querying
* an empty queue with the {@link #query()} method.
*
* @return <code>null</code>
*/
@Override
public E createQueryData() {
return null;
}
private void fixAfterPushComparator(E e) {
while (!minQueue.isEmpty() && cmp.compare(e, minQueue.peekLast()) < 0)
minQueue.pollLast();
minQueue.addLast(e);
assert minQueue.isEmpty() == queue.isEmpty();
}
@SuppressWarnings("unchecked")
private void fixAfterPushComparable(Comparable<? super E> e) {
while (!minQueue.isEmpty() && e.compareTo(minQueue.peekLast()) < 0)
minQueue.pollLast();
minQueue.addLast((E) e);
assert minQueue.isEmpty() == queue.isEmpty();
}
private void fixAfterPushAllComparator(Collection<? extends E> c) {
if (c.isEmpty())
return ;
// find the minimum of the added elements and call fixAfterPush(min)
Iterator<? extends E> it = c.iterator();
E min = it.next();
while (it.hasNext()) {
E e = it.next();
if (cmp.compare(e, min) < 0)
min = e;
}
fixAfterPushComparator(min);
assert minQueue.isEmpty() == queue.isEmpty();
}
@SuppressWarnings("unchecked")
private void fixAfterPushAllComparable(
Collection<? extends E> c) {
if (c.isEmpty())
return ;
// find the minimum of the added elements and call fixAfterPush(min)
Iterator<? extends E> it = c.iterator();
E min = it.next();
while (it.hasNext()) {
Comparable<? super E> e = (Comparable<? super E>) it.next();
if (e.compareTo(min) < 0)
min = (E) e;
}
fixAfterPushComparable((Comparable<? super E>) min);
assert minQueue.isEmpty() == queue.isEmpty();
}
private void fixAfterPopComparator(E e) {
if (!minQueue.isEmpty()
&& cmp.compare(minQueue.peek(), e) == 0)
minQueue.remove();
assert minQueue.isEmpty() == queue.isEmpty();
}
private void fixAfterPopComparable(Comparable<? super E> e) {
if (!minQueue.isEmpty()
&& e.compareTo(minQueue.peek()) == 0)
minQueue.remove();
assert minQueue.isEmpty() == queue.isEmpty();
}
// serialization stuff
private void writeObject(ObjectOutputStream stream) throws IOException {
stream.defaultWriteObject();
}
private void readObject(ObjectInputStream stream) throws IOException,
ClassNotFoundException {
stream.defaultReadObject();
if (cmp != null)
fixAfterPushAllComparator(this);
else
fixAfterPushAllComparable(this);
}
private static final class NoRemoveItr<E> implements Iterator<E> {
final Iterator<E> it;
NoRemoveItr(Iterator<E> it) {
this.it = it;
}
@Override
public boolean hasNext() {
return it.hasNext();
}
@Override
public E next() {
return it.next();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
}