简体   繁体   English

是否有具有固定容量和自定义比较器的 PriorityQueue 实现?

[英]Is there a PriorityQueue implementation with fixed capacity and custom comparator?

Related questions:相关问题:

I have a very large data set (more than 5 millions items) and I need to get N largest items from it.我有一个非常大的数据集(超过 500 万个项目),我需要从中获取N 个最大的项目。 The most natural way to do it is to use heap/priority queue storing only top N items .最自然的方法是使用只存储前 N 个项目的堆/优先级队列。 There are several good implementations of priority queue for JVM (Scala/Java), namely: JVM (Scala/Java) 优先级队列有几个很好的实现,即:

First 2 are nice, but they store all the items, which in my case gives critical memory overhead.前 2 个很好,但它们存储所有项目,在我的情况下,这会产生关键的内存开销。 Third (Lucene implementation) doesn't have such a drawback, but as I can see from documentation it also doesn't support custom comparator, which makes it useless for me.第三(Lucene 实现)没有这样的缺点,但正如我从文档中看到的那样,它也不支持自定义比较器,这对我来说毫无用处。

So, my question is: Is there a PriorityQueue implementation with fixed capacity and custom comparator ?所以,我的问题是:是否有具有固定容量自定义比较器PriorityQueue实现

UPD.更新。 Finally I've created my own implementation, based on Peter's answer:最后,我根据 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;
            }
        }
    }
}

(where NaturalComparator is taken from this question) (其中NaturalComparator取自这个问题)

How can you say Lucene's doesn't support a custom comparator?你怎么能说 Lucene 不支持自定义比较器?

Its abstract and you must implement the abstract method lessThan(T a, T b)它的抽象,你必须实现抽象方法lessThan(T a, T b)

您可以使用带有自定义比较器的 SortedSet 例如 TreeSet 并在大小达到 N 时删除最小的。

Though an old question but it may be helpful to somebody else.虽然是一个老问题,但它可能对其他人有帮助。 You can use minMaxPriorityQueue of Google's Java library guava.您可以使用 Google 的 Java 库番石榴的minMaxPriorityQueue

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.编辑:似乎使用TreeSet毕竟不是很有效,因为对first()的调用似乎需要次线性时间。 I changed the TreeSet to a PriorityQueue .我将TreeSet更改为PriorityQueue The modified add() method looks like this:修改后的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);
    }

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.不同之处在于比较器,但如果您从PriorityQueue扩展,您将拥有它。 And on each addition check if you haven't reached the limit, and if you have - drop the last item.并在每次添加时检查您是否尚未达到限制,如果已达到 - 删除最后一项。

Exactly what I was looking for.正是我要找的。 However, the implementation contains a bug:但是,该实现包含一个错误:

Namely: if elementsLeft > 0 and e is already contained in the TreeSet.即:如果 elementsLeft > 0 并且 e 已经包含在 TreeSet 中。 In this case, elementsLeft is decreased, but the number of elements in the TreeSet stays the same.在这种情况下,elementsLeft 减少了,但 TreeSet 中的元素数量保持不变。

I would suggest to replace the corresponding lines in the add() method by我建议将 add() 方法中的相应行替换为

        } else if (elementsLeft > 0) {
        // queue isn't full => add element and decrement elementsLeft
        boolean added = super.add(e);
        if (added) {
            elementsLeft--;
        }
        return added;

Try this code:试试这个代码:

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;

    }
}

}

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.您可以使用 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);
    }
}

Well, quite an old question, but I'm confused why a simpler solution hasn't been suggested yet.嗯,一个很老的问题,但我很困惑为什么还没有提出更简单的解决方案。

Unless I'm missing something, this can be trivially solved using a min-heap (Java's default PriorityQueue implementation) with a slight twist in that the moment the size of the PriorityQueue becomes greater than k(ie if we're trying to store the top k elements), you poll the head.除非我遗漏了一些东西,否则可以使用最小堆(Java 的默认 PriorityQueue 实现)轻松解决这个问题,但在 PriorityQueue 的大小变得大于 k 的那一刻(即,如果我们试图存储前k个元素),你轮询头部。

Here's an example of what I mean这是我的意思的一个例子

public void storeKLargest(int[] nums, int k) {
    PriorityQueue<Integer> pq = new PriorityQueue<>(k+1);
    for(int num: nums){
        if(pq.size() < k || pq.peek() < num)
            pq.offer(num);
        if(pq.size() == k+1)
            pq.poll();
    }
}

I used a PriorityQueue of Integer, but it's simple enough to replace it with a custom object and feed in a custom Comparator.我使用了 Integer 的 PriorityQueue,但它很简单,可以用自定义对象替换它并输入自定义比较器。

Unless I'm missing something obvious, I suppose this is what the OP was looking for.除非我遗漏了一些明显的东西,否则我想这就是 OP 正在寻找的东西。

Create a PriorityQueue that has size limit.创建一个具有大小限制的 PriorityQueue。 It stores N max numbers.它存储 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);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM