Related questions:
Here is one I put together if you have guava. I think it is is pretty complete. Let me know if I missed something.
You can use the gauva ForwardingBlockingQueue so you don't have to map all the other methods.
import com.google.common.util.concurrent.ForwardingBlockingQueue;
public class PriorityBlockingQueueDecorator<E> extends
ForwardingBlockingQueue<E> {
public static final class QueueFullException extends IllegalStateException {
private static final long serialVersionUID = -9218216017510478441L;
}
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private int maxSize;
private PriorityBlockingQueue<E> delegate;
public PriorityBlockingQueueDecorator(PriorityBlockingQueue<E> delegate) {
this(MAX_ARRAY_SIZE, delegate);
}
public PriorityBlockingQueueDecorator(int maxSize,
PriorityBlockingQueue<E> delegate) {
this.maxSize = maxSize;
this.delegate = delegate;
}
@Override
protected BlockingQueue<E> delegate() {
return delegate;
}
@Override
public boolean add(E element) {
return offer(element);
}
@Override
public boolean addAll(Collection<? extends E> collection) {
boolean modified = false;
for (E e : collection)
if (add(e))
modified = true;
return modified;
}
@Override
public boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException {
return offer(e);
}
@Override
public boolean offer(E o) {
if (maxSize > size()) {
throw new QueueFullException();
}
return super.offer(o);
}
}
Create a PriorityQueue that has size limit. It stores N max numbers.
import java.util.*;
class Demo
{
public static <E extends Comparable<E>> PriorityQueue<E> getPq(final int n, Comparator<E> comparator)
{
return new PriorityQueue<E>(comparator)
{
boolean full()
{
return size() >= n;
}
@Override
public boolean add(E e)
{
if (!full())
{
return super.add(e);
}
else if (peek().compareTo(e) < 0)
{
poll();
return super.add(e);
}
return false;
}
@Override
public boolean offer(E e)
{
if (!full())
{
return super.offer(e);
}
else if (peek().compareTo(e) < 0)
{
poll();
return super.offer(e);
}
return false;
}
};
}
public static void printq(PriorityQueue pq)
{
Object o = null;
while ((o = pq.poll()) != null)
{
System.out.println(o);
}
}
public static void main (String[] args)
{
PriorityQueue<Integer> pq = getPq(2, new Comparator<Integer>(){
@Override
public int compare(Integer i1, Integer i2)
{
return i1.compareTo(i2);
}
});
pq.add(4);
pq.add(1);
pq.add(5);
pq.add(2);
printq(pq);
}
}
Below is the implementation I used before. Complies with Peter's suggestion.
public @interface NonThreadSafe {
}
/**
* A priority queue implementation with a fixed size based on a {@link TreeMap}.
* The number of elements in the queue will be at most {@code maxSize}.
* Once the number of elements in the queue reaches {@code maxSize}, trying to add a new element
* will remove the greatest element in the queue if the new element is less than or equal to
* the current greatest element. The queue will not be modified otherwise.
*/
@NonThreadSafe
public static class FixedSizePriorityQueue<E> {
private final TreeSet<E> treeSet; /* backing data structure */
private final Comparator<? super E> comparator;
private final int maxSize;
/**
* Constructs a {@link FixedSizePriorityQueue} with the specified {@code maxSize}
* and {@code comparator}.
*
* @param maxSize - The maximum size the queue can reach, must be a positive integer.
* @param comparator - The comparator to be used to compare the elements in the queue, must be non-null.
*/
public FixedSizePriorityQueue(final int maxSize, final Comparator<? super E> comparator) {
super();
if (maxSize <= 0) {
throw new IllegalArgumentException("maxSize = " + maxSize + "; expected a positive integer.");
}
if (comparator == null) {
throw new NullPointerException("Comparator is null.");
}
this.treeSet = new TreeSet<E>(comparator);
this.comparator = treeSet.comparator();
this.maxSize = maxSize;
}
/**
* Adds an element to the queue. If the queue contains {@code maxSize} elements, {@code e} will
* be compared to the greatest element in the queue using {@code comparator}.
* If {@code e} is less than or equal to the greatest element, that element will be removed and
* {@code e} will be added instead. Otherwise, the queue will not be modified
* and {@code e} will not be added.
*
* @param e - Element to be added, must be non-null.
*/
public void add(final E e) {
if (e == null) {
throw new NullPointerException("e is null.");
}
if (maxSize <= treeSet.size()) {
final E firstElm = treeSet.first();
if (comparator.compare(e, firstElm) < 1) {
return;
} else {
treeSet.pollFirst();
}
}
treeSet.add(e);
}
/**
* @return Returns a sorted view of the queue as a {@link Collections#unmodifiableList(java.util.List)}
* unmodifiableList.
*/
public List<E> asList() {
return Collections.unmodifiableList(new ArrayList<E>(treeSet));
}
}
I would appreciate any feedback btw.
EDIT: It seems like using a TreeSet
is not very efficient after all because the calls to first()
seem to take sublinear time. I changed the TreeSet
to a PriorityQueue
. The modified add()
method looks like this:
/**
* Adds an element to the queue. If the queue contains {@code maxSize} elements, {@code e} will
* be compared to the lowest element in the queue using {@code comparator}.
* If {@code e} is greater than or equal to the lowest element, that element will be removed and
* {@code e} will be added instead. Otherwise, the queue will not be modified
* and {@code e} will not be added.
*
* @param e - Element to be added, must be non-null.
*/
public void add(final E e) {
if (e == null) {
throw new NullPointerException("e is null.");
}
if (maxSize <= priorityQueue.size()) {
final E firstElm = priorityQueue.peek();
if (comparator.compare(e, firstElm) < 1) {
return;
} else {
priorityQueue.poll();
}
}
priorityQueue.add(e);
}
You could use a SortedSet e.g. TreeSet with a custom comparator and remove the smallest when the size reachs N.
I can't think of a ready-to-use one, but you can check my implementation of this collection with similar requirements.
The difference is the comparator, but if you extend from PriorityQueue
you'll have it. And on each addition check if you haven't reached the limit, and if you have - drop the last item.
How can you say Lucene's doesn't support a custom comparator?
Its abstract and you must implement the abstract method lessThan(T a, T b)