関連する質問:
非常に大きなデータセット(500万以上のアイテム)があり、そこからN最大のアイテムを取得する必要があります。これを行う最も自然な方法は、ヒープ/優先度キューを使用して上位Nアイテムのみを格納することです。つまり、JVM(Scala/Java)の優先度キューにはいくつかの優れた実装があります。
最初の2つはいいですが、すべてのアイテムを保存します。私の場合、重要なメモリオーバーヘッドが発生します。 3番目(Lucene実装)にはそのような欠点はありませんが、ドキュメントからわかるように、カスタムコンパレーターをサポートしていないため、私にとっては役に立たないものになっています。
だから、私の質問は:PriorityQueue
実装固定容量とカスタムコンパレータ?
UPD。最後に、Peterの回答に基づいて、独自の実装を作成しました。
public class FixedSizePriorityQueue<E> extends TreeSet<E> {
private int elementsLeft;
public FixedSizePriorityQueue(int maxSize) {
super(new NaturalComparator());
this.elementsLeft = maxSize;
}
public FixedSizePriorityQueue(int maxSize, Comparator<E> comparator) {
super(comparator);
this.elementsLeft = maxSize;
}
/**
* @return true if element was added, false otherwise
* */
@Override
public boolean add(E e) {
if (elementsLeft == 0 && size() == 0) {
// max size was initiated to zero => just return false
return false;
} else if (elementsLeft > 0) {
// queue isn't full => add element and decrement elementsLeft
boolean added = super.add(e);
if (added) {
elementsLeft--;
}
return added;
} else {
// there is already 1 or more elements => compare to the least
int compared = super.comparator().compare(e, this.first());
if (compared == 1) {
// new element is larger than the least in queue => pull the least and add new one to queue
pollFirst();
super.add(e);
return true;
} else {
// new element is less than the least in queue => return false
return false;
}
}
}
}
(ここでNaturalComparator
は this 質問から取得されます)
あなたはソートセットを使用することができます。カスタムコンパレーターを含むTreeSet。サイズがNに達したときに最小のものを削除します。
Luceneはカスタムコンパレーターをサポートしていないとどうして言えますか?
その抽象であり、抽象メソッドlessThan(T a, T b)
を実装する必要があります
古い質問ですが、それは他の誰かに役立つかもしれません。 GoogleのJavaライブラリグアバの minMaxPriorityQueue を使用できます。
すぐに使えるものは考えられませんが、同様の要件でこのコレクションの my implementation を確認できます。
違いはコンパレータですが、PriorityQueue
から拡張すると、それが得られます。そして、追加するたびに、制限に達していないかどうかを確認し、達している場合は、最後のアイテムをドロップしてください。
以下は、以前使用した実装です。ピーターの提案に従います。
_ 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));
}
}
_
私はフィードバックをいただければ幸いです。
EDIT:first()
への呼び出しは準線形時間をとるように見えるため、TreeSet
を使用するのは結局効率が悪いようです。 TreeSet
をPriorityQueue
に変更しました。変更されたadd()
メソッドは次のようになります。
_ /**
* 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);
}
_
まさに私が探していたもの。ただし、実装にはバグが含まれています。
つまり、elementsLeft> 0であり、eがすでにTreeSetに含まれている場合です。この場合、elementsLeftは減少しますが、TreeSet内の要素の数は変わりません。
Add()メソッドの対応する行を次のように置き換えることをお勧めします
} else if (elementsLeft > 0) {
// queue isn't full => add element and decrement elementsLeft
boolean added = super.add(e);
if (added) {
elementsLeft--;
}
return added;
このコードを試してください:
public class BoundedPQueue<E extends Comparable<E>> {
/**
* Lock used for all public operations
*/
private final ReentrantLock lock;
PriorityBlockingQueue<E> queue ;
int size = 0;
public BoundedPQueue(int capacity){
queue = new PriorityBlockingQueue<E>(capacity, new CustomComparator<E>());
size = capacity;
this.lock = new ReentrantLock();
}
public boolean offer(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
E vl = null;
if(queue.size()>= size) {
vl= queue.poll();
if(vl.compareTo(e)<0)
e=vl;
}
try {
return queue.offer(e);
} finally {
lock.unlock();
}
}
public E poll() {
return queue.poll();
}
public static class CustomComparator<E extends Comparable<E>> implements Comparator<E> {
@Override
public int compare(E o1, E o2) {
//give me a max heap
return o1.compareTo(o2) *-1;
}
}
}
グアバをお持ちならここにまとめました。かなり完成していると思います。何かを見逃した場合はお知らせください。
Gauva ForwardingBlockingQueueを使用できるので、他のすべてのメソッドをマップする必要はありません。
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);
}
}
サイズ制限のあるPriorityQueueを作成します。 N個の最大数を格納します。
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);
}
}