wake-up-neo.com

Gibt es eine PriorityQueue-Implementierung mit fester Kapazität und benutzerdefiniertem Vergleicher?

Verwandte Fragen: 

Ich habe einen sehr großen Datensatz (mehr als 5 Millionen Elemente) und ich muss N die größten Elemente davon erhalten. Die natürlichste Methode ist die Verwendung der Heap/Priority-Warteschlange , die nur die Top-N-Elemente speichert. Es gibt mehrere gute Implementierungen der Prioritätswarteschlange für JVM (Scala/Java), nämlich: 

Die ersten beiden sind Nizza, aber sie speichern alle Elemente, was in meinem Fall zu einem kritischen Speicher-Overhead führt. Drittens (Lucene-Implementierung) hat keinen solchen Nachteil, aber wie ich in der Dokumentation sehen kann, unterstützt er auch keinen benutzerdefinierten Vergleicher, was ihn für mich nutzlos macht. 

Meine Frage ist also: Gibt es eine PriorityQueue-Implementierung mit fester Kapazität und benutzerdefiniertem Komparator?

UPD. Schließlich habe ich meine eigene Implementierung erstellt, basierend auf Peters Antwort:

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;
            }
        }
    }
}

(wobei NaturalComparator aus this question stammt)

42
ffriend

Sie können ein SortedSet verwenden, z. TreeSet mit einem benutzerdefinierten Komparator und entfernt den kleinsten, wenn die Größe N erreicht.

12
Peter Lawrey

Wie können Sie sagen, dass Lucene einen benutzerdefinierten Vergleicher nicht unterstützt?

Es ist abstrakt und Sie müssen die abstrakte Methode lessThan(T a, T b) implementieren.

16
Robert Muir

Obwohl es eine alte Frage ist, die für andere hilfreich sein kann .. Sie können minMaxPriorityQueue von Googles Java-Bibliothek Guava verwenden. 

14
Terminal

Ich kann mir keine gebrauchsfertige vorstellen, aber Sie können meine Implementierung dieser Sammlung mit ähnlichen Anforderungen überprüfen.

Der Unterschied ist der Komparator, aber wenn Sie von PriorityQueue gehen, haben Sie es. Prüfen Sie bei jeder Addition, ob Sie das Limit noch nicht erreicht haben, und wenn Sie den letzten Eintrag gelöscht haben. 

4
Bozho

Unten ist die Implementierung, die ich zuvor verwendet habe. Entspricht dem Vorschlag von Peter.

 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));
    }
}

Über Rückmeldungen würde ich mich freuen.

EDIT: Die Verwendung einer TreeSet scheint nicht sehr effizient zu sein, da die Aufrufe an first() scheinbar sublinear dauern. Ich änderte die TreeSet in eine PriorityQueue. Die modifizierte add()-Methode sieht folgendermaßen aus:

   /**
     * 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);
    }
4

Genau das, wonach ich gesucht habe. Die Implementierung enthält jedoch einen Fehler:

Wenn nämlich elementsLeft> 0 und e bereits im TreeSet enthalten sind ..__ In diesem Fall wird elementsLeft verkleinert, die Anzahl der Elemente im TreeSet bleibt jedoch gleich. 

Ich würde vorschlagen, die entsprechenden Zeilen in der add () -Methode durch zu ersetzen

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

Versuchen Sie diesen 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;

    }
}

}
1
foo_bar

Hier ist eine, die ich zusammengestellt habe, wenn Sie Guave haben. Ich denke es ist ziemlich komplett. Sag mir Bescheid, wenn ich etwas verpasst habe. 

Sie können die gauva ForwardingBlockingQueue verwenden, damit Sie nicht alle anderen Methoden zuordnen müssen. 

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);
    }
}
1
Chris Hinshaw

Erstellen Sie eine PriorityQueue mit Größenbeschränkung. Es speichert N max Zahlen.

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);
    }
}
0
display_name