Come si usa PriorityQueue?


Risposte:


449

Usa il sovraccarico del costruttore che accetta a Comparator<? super E> comparatore passa un comparatore che confronta in modo appropriato per il tuo ordinamento. Se fornisci un esempio di come desideri ordinare, possiamo fornire del codice di esempio per implementare il comparatore se non sei sicuro. (È piuttosto semplice però.)

Come è stato detto altrove: offere addsono solo diverse implementazioni del metodo di interfaccia. Nella fonte JDK che ho, addchiamate offer. Sebbene adde offerabbiano un comportamento potenzialmente diverso in generale a causa della capacità offerdi indicare che il valore non può essere aggiunto a causa di limiti di dimensione, questa differenza è irrilevante in PriorityQueuecui è illimitato.

Ecco un esempio di ordinamento di una priorità prioritaria per lunghezza della stringa:

// Test.java
import java.util.Comparator;
import java.util.PriorityQueue;

public class Test {
    public static void main(String[] args) {
        Comparator<String> comparator = new StringLengthComparator();
        PriorityQueue<String> queue = new PriorityQueue<String>(10, comparator);
        queue.add("short");
        queue.add("very long indeed");
        queue.add("medium");
        while (queue.size() != 0) {
            System.out.println(queue.remove());
        }
    }
}

// StringLengthComparator.java
import java.util.Comparator;

public class StringLengthComparator implements Comparator<String> {
    @Override
    public int compare(String x, String y) {
        // Assume neither string is null. Real code should
        // probably be more robust
        // You could also just return x.length() - y.length(),
        // which would be more efficient.
        if (x.length() < y.length()) {
            return -1;
        }
        if (x.length() > y.length()) {
            return 1;
        }
        return 0;
    }
}

Ecco l'output:

corto

medio

molto a lungo


7
Hmm ... ho appena notato ... Significa che potrei implementare anche Comparable anche sulla mia classe?
Svish,

7
Si, si. Non lo farei se non ci fosse un solo ordinamento naturale per la tua classe. Se c'è, è la cosa giusta da fare :)
Jon Skeet,

8
L' compareimplementazione non dovrebbe essere return x.length() - y.length()? (Evita la previsione del ramo)
Franky

7
@Franky: Potrebbe essere, sì - anche se direi che è leggermente più difficile da capire, e lo scopo della risposta è dimostrare come funziona. Aggiungerò un commento però.
Jon Skeet,

2
@KarelG: Non penso che importi troppo, purché tu sia consapevole delle differenze. Penso che se stai usando add()per l'operazione di aggiunta, allora mi remove()sento sensato; se stavo usando offer()probabilmente userei poll()... ma questa è solo una preferenza personale.
Jon Skeet,

68

Soluzione Java 8

Possiamo usare lambda expressiono method referenceintrodurre in Java 8. Nel caso in cui abbiamo archiviato alcuni valori String nella coda di priorità (con capacità 5) possiamo fornire un comparatore in linea (basato sulla lunghezza della stringa):

Usando l'espressione lambda

PriorityQueue<String> pq=
                    new PriorityQueue<String>(5,(a,b) -> a.length() - b.length());

Utilizzando il metodo di riferimento

PriorityQueue<String> pq=
                new PriorityQueue<String>(5, Comparator.comparing(String::length));

Quindi possiamo usarne uno come:

public static void main(String[] args) {
        PriorityQueue<String> pq=
                new PriorityQueue<String>(5, (a,b) -> a.length() - b.length());
       // or pq = new PriorityQueue<String>(5, Comparator.comparing(String::length));
        pq.add("Apple");
        pq.add("PineApple");
        pq.add("Custard Apple");
        while (pq.size() != 0)
        {
            System.out.println(pq.remove());
        }
    }

Questo stamperà:

Apple
PineApple
Custard Apple

Per invertire l'ordine (per cambiarlo nella coda con priorità massima) è sufficiente modificare l'ordine nel comparatore in linea o utilizzare reversedcome:

PriorityQueue<String> pq = new PriorityQueue<String>(5, 
                             Comparator.comparing(String::length).reversed());

Possiamo anche usare Collections.reverseOrder:

PriorityQueue<Integer> pqInt = new PriorityQueue<>(10, Collections.reverseOrder());
PriorityQueue<String> pq = new PriorityQueue<String>(5, 
                Collections.reverseOrder(Comparator.comparing(String::length))

Quindi possiamo vedere che Collections.reverseOrderè sovraccarico per prendere un comparatore che può essere utile per oggetti personalizzati. Le reversedutilizza in realtà Collections.reverseOrder:

default Comparator<T> reversed() {
    return Collections.reverseOrder(this);
}

offer () vs add ()

Come da doc

Il metodo di offerta inserisce un elemento, se possibile, altrimenti restituisce false. Ciò differisce dal metodo Collection.add, che può non riuscire ad aggiungere un elemento solo generando un'eccezione non selezionata. Il metodo di offerta è progettato per l'uso quando l'errore è un evento normale, piuttosto che eccezionale, ad esempio in code a capacità fissa (o "limitate").

Quando si utilizza una coda a capacità limitata, offer () è generalmente preferibile aggiungere (), che può non riuscire a inserire un elemento solo lanciando un'eccezione. E PriorityQueue è una coda di priorità illimitata basata su un heap di priorità.


Sto assumendo che 5indica la capacità iniziale della coda?
Neil

1
@Neil Sì, l'ho reso più esplicito nella risposta ora :)
akhil_mittal

1
L'ottava versione di Java è stata la cosa migliore che sia mai accaduta alla lingua
GabrielBB,

1
Spiegazione molto bella con esempio lucido.
Vishwa Ratna,

24

Basta passare appropriato Comparatoral costruttore :

PriorityQueue(int initialCapacity, Comparator<? super E> comparator)

L'unica differenza tra offere addè l'interfaccia a cui appartengono. offerappartiene a Queue<E>, mentre addoriginariamente è visto Collection<E>nell'interfaccia. A parte questo, entrambi i metodi fanno esattamente la stessa cosa: inserire l'elemento specificato nella coda di priorità.


7
In particolare, add () genera un'eccezione se le restrizioni di capacità impediscono che l'elemento venga aggiunto alla coda mentre l'offerta restituisce false. Poiché PriorityQueues non ha una capacità massima, la differenza è discutibile.
James,

Questa è una chiara distinzione tra add () e offer () .. E add () doveva essere implementato comunque!
Whitehat

19

da API coda :

Il metodo di offerta inserisce un elemento, se possibile, altrimenti restituisce false. Ciò differisce dal metodo Collection.add, che può non riuscire ad aggiungere un elemento solo generando un'eccezione non selezionata. Il metodo di offerta è progettato per l'uso quando l'errore è un evento normale, piuttosto che eccezionale, ad esempio in code a capacità fissa (o "limitate").


12

non diverso, come dichiarato in javadoc:

public boolean add(E e) {
    return offer(e);
}

6

Solo per rispondere alla domanda add()vs offer()(dal momento che l'altro ha una risposta perfettamente imo, e questo potrebbe non essere):

Secondo JavaDoc sulla coda dell'interfaccia , "Il metodo di offerta inserisce un elemento, se possibile, altrimenti restituisce false. Ciò differisce dal metodo Collection.add, che può non riuscire ad aggiungere un elemento solo generando un'eccezione non selezionata. Il metodo di offerta è progettato per utilizzare quando l'errore è un'occorrenza normale, piuttosto che eccezionale, ad esempio in code a capacità fissa (o "limitate"). "

Ciò significa che se puoi aggiungere l'elemento (che dovrebbe sempre essere il caso in PriorityQueue), funzionano esattamente allo stesso modo. Ma se non riesci ad aggiungere l'elemento, offer()ti darà un falseritorno bello e carino , mentre add()genera una brutta eccezione non controllata che non vuoi nel tuo codice. Se la mancata aggiunta indica che il codice funziona come previsto e / o è qualcosa che verifichi normalmente, utilizza offer(). Se la mancata aggiunta indica che qualcosa non funziona, utilizzare add()e gestire l'eccezione risultante generata in base alle specifiche dell'interfaccia Collection .

Entrambi sono implementati in questo modo per riempire il contratto sull'interfaccia Queue che specifica i offer()fallimenti restituendo un false( metodo preferito nelle code a capacità limitata ) e mantengono anche il contratto sull'interfaccia Collection che specifica add()fallisce sempre lanciando un'eccezione .

Comunque, spero che chiarisca almeno quella parte della domanda.


6

Qui, possiamo definire un comparatore definito dall'utente:

Sotto il codice:

 import java.util.*;
 import java.util.Collections;
 import java.util.Comparator; 


 class Checker implements Comparator<String>
 {
    public int compare(String str1, String str2)
    {
        if (str1.length() < str2.length()) return -1;
        else                               return 1;
    }
 }


class Main
{  
   public static void main(String args[])
    {  
      PriorityQueue<String> queue=new PriorityQueue<String>(5, new Checker());  
      queue.add("india");  
      queue.add("bangladesh");  
      queue.add("pakistan");  

      while (queue.size() != 0)
      {
         System.out.printf("%s\n",queue.remove());
      }
   }  
}  

Produzione :

   india                                               
   pakistan                                         
   bangladesh

Differenza tra l'offerta e i metodi di aggiunta: link


1
e se fossero uguali?
nycynik,

4

Passalo a Comparator. Inserisci il tipo desiderato al posto diT

Utilizzo di lambdas (Java 8+):

int initialCapacity = 10;
PriorityQueue<T> pq = new PriorityQueue<>(initialCapacity, (e1, e2) -> { return e1.compareTo(e2); });

Modo classico, usando una classe anonima:

int initialCapacity = 10;
PriorityQueue<T> pq = new PriorityQueue<>(initialCapacity, new Comparator<T> () {

    @Override
    public int compare(T e1, T e2) {
        return e1.compareTo(e2);
    }

});

Per ordinare in ordine inverso, basta scambiare e1, e2.


3

Mi chiedevo anche l'ordine di stampa. Considera questo caso, ad esempio:

Per una coda prioritaria:

PriorityQueue<String> pq3 = new PriorityQueue<String>();

Questo codice:

pq3.offer("a");
pq3.offer("A");

può stampare diversamente da:

String[] sa = {"a", "A"}; 
for(String s : sa)   
   pq3.offer(s);

Ho trovato la risposta da una discussione su un altro forum , in cui un utente ha detto, "i metodi offer () / add () inseriscono solo l'elemento nella coda. Se vuoi un ordine prevedibile, dovresti usare peek / poll che restituisce la testa della coda ".


3

In alternativa a utilizzare Comparator, si può anche avere la classe che si sta utilizzando nel vostro PriorityQueue implementareComparable (e corrispondentemente l'override del compareTometodo).

Si noti che in genere è meglio usare Comparableinvece Comparatorche se quell'ordinamento è l'ordinamento intuitivo dell'oggetto - se, ad esempio, si dispone di un caso d'uso per ordinare gli Personoggetti per età, probabilmente è meglio semplicemente utilizzare Comparatorinvece.

import java.lang.Comparable;
import java.util.PriorityQueue;

class Test
{
    public static void main(String[] args)
    {
        PriorityQueue<MyClass> queue = new PriorityQueue<MyClass>();
        queue.add(new MyClass(2, "short"));
        queue.add(new MyClass(2, "very long indeed"));
        queue.add(new MyClass(1, "medium"));
        queue.add(new MyClass(1, "very long indeed"));
        queue.add(new MyClass(2, "medium"));
        queue.add(new MyClass(1, "short"));
        while (queue.size() != 0)
            System.out.println(queue.remove());
    }
}
class MyClass implements Comparable<MyClass>
{
    int sortFirst;
    String sortByLength;

    public MyClass(int sortFirst, String sortByLength)
    {
        this.sortFirst = sortFirst;
        this.sortByLength = sortByLength;
    }

    @Override
    public int compareTo(MyClass other)
    {
        if (sortFirst != other.sortFirst)
            return Integer.compare(sortFirst, other.sortFirst);
        else
            return Integer.compare(sortByLength.length(), other.sortByLength.length());
    }

    public String toString()
    {
        return sortFirst + ", " + sortByLength;
    }
}

Produzione:

1, short
1, medium
1, very long indeed
2, short
2, medium
2, very long indeed

1

La coda di priorità ha una priorità assegnata a ciascun elemento, l'elemento con la priorità più alta appare nella parte superiore della coda. Ora, dipende da te come desideri assegnare la priorità a ciascuno degli elementi. In caso contrario, Java lo farà nel modo predefinito. All'elemento con il valore minimo viene assegnata la priorità più alta e quindi viene rimosso per primo dalla coda. Se ci sono più elementi con la stessa priorità massima, il pareggio viene interrotto arbitrariamente. Puoi anche specificare un ordine usando Comparator nel costruttore PriorityQueue(initialCapacity, comparator)

Codice di esempio:

PriorityQueue<String> queue1 = new PriorityQueue<>();
queue1.offer("Oklahoma");
queue1.offer("Indiana");
queue1.offer("Georgia");
queue1.offer("Texas");
System.out.println("Priority queue using Comparable:");
while (queue1.size() > 0) {
    System.out.print(queue1.remove() + " ");
}
PriorityQueue<String> queue2 = new PriorityQueue(4, Collections.reverseOrder());
queue2.offer("Oklahoma");
queue2.offer("Indiana");
queue2.offer("Georgia");
queue2.offer("Texas");
System.out.println("\nPriority queue using Comparator:");
while (queue2.size() > 0) {
    System.out.print(queue2.remove() + " ");
}

Produzione:

Priority queue using Comparable:
Georgia Indiana Oklahoma Texas 
Priority queue using Comparator:
Texas Oklahoma Indiana Georgia 

Altrimenti, puoi anche definire un comparatore personalizzato:

import java.util.Comparator;

public class StringLengthComparator implements Comparator<String>
{
    @Override
    public int compare(String x, String y)
    {
        //Your Own Logic
    }
}

1

Ecco il semplice esempio che puoi usare per l'apprendimento iniziale:

import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Random;

public class PQExample {

    public static void main(String[] args) {
        //PriorityQueue with Comparator
        Queue<Customer> cpq = new PriorityQueue<>(7, idComp);
        addToQueue(cpq);
        pollFromQueue(cpq);
    }

    public static Comparator<Customer> idComp = new Comparator<Customer>(){

        @Override
        public int compare(Customer o1, Customer o2) {
            return (int) (o1.getId() - o2.getId());
        }

    };

    //utility method to add random data to Queue
    private static void addToQueue(Queue<Customer> cq){
        Random rand = new Random();
        for(int i=0;i<7;i++){
            int id = rand.nextInt(100);
            cq.add(new Customer(id, "KV"+id));
        }
    }


    private static void pollFromQueue(Queue<Customer> cq){
        while(true){
            Customer c = cq.poll();
            if(c == null) break;
            System.out.println("Customer Polled : "+c.getId() + " "+ c.getName());
        }
    }

}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.