Bir PriorityQueue'yu nasıl kullanırım?


Yanıtlar:


449

Comparator<? super E> comparatorSıralama düzeniniz için uygun şekilde karşılaştırılan bir karşılaştırıcıyı alan ve geçiren yapıcı aşırı yükünü kullanın . Nasıl sıralamak istediğinize ilişkin bir örnek verirseniz, emin değilseniz karşılaştırıcıyı uygulamak için bazı örnek kod sağlayabiliriz. (Yine de oldukça basit.)

Başka bir yerde söylendiği gibi: offerve addsadece farklı arayüz yöntemi uygulamaları. JDK kaynağında, addçağrılarım var offer. Her ne kadar addve offersahip potansiyel yeteneğinin genel Due farklı davranış offerdeğeri hacim sınırlamalarına bağlı olarak ilave edilemez olduğunu göstermek için, bu fark, önemli değildir PriorityQueueolan sonsuz.

Dize uzunluğuna göre öncelik sırası sıralamasına bir örnek:

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

İşte çıktı:

kısa

orta

gerçekten çok uzun


7
Hmm ... az önce fark edildi ... priorityQueue.comparator () "Bu koleksiyonu sipariş etmek için kullanılan karşılaştırıcıyı veya bu koleksiyon öğelerinin doğal sırasına göre (Karşılaştırılabilir kullanarak) sıralanırsa null değerini döndürür." Bu, Sınıfımda da Karşılaştırılabilir'i uygulayabileceğim anlamına mı geliyor?
Svish

7
Yapabilirsin, evet. Sınıfınız için tek bir doğal sıralama düzeni olmasaydı bunu yapmazdım. Varsa, yapılacak doğru şey bu :)
Jon Skeet

8
compareUygulama sadece değil return x.length() - y.length()mi? (Şube tahmininden kaçınır)
Franky

7
@Franky: Evet, evet - bunun anlaşılması biraz daha zor olduğunu söylesem de, cevabın amacı bunun nasıl çalıştığını göstermek. Yine de bir yorum ekleyeceğim.
Jon Skeet

2
@KarelG: Farklılıkların farkında olduğunuz sürece bunun çok önemli olduğunu düşünmüyorum . Eğer add()ekleme işlemi için kullanıyorsanız , o zaman remove()mantıklı geliyor düşünüyorum; Eğer offer()kullansaydım muhtemelen kullanardım poll()... ama bu sadece kişisel bir tercih.
Jon Skeet

68

Java 8 çözümü

Java 8'i kullanabilir lambda expressionya da kullanabiliriz method reference. Öncelik Kuyruğunda saklanan bazı String değerlerimiz olması durumunda (kapasite 5'e sahip) satır içi karşılaştırıcı (String uzunluğuna bağlı olarak) sağlayabiliriz:

Lambda ifadesini kullanma

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

Yöntem referansını kullanma

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

Sonra bunlardan herhangi birini kullanabiliriz:

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

Bu yazdırılacak:

Apple
PineApple
Custard Apple

Sırayı tersine çevirmek (maksimum öncelik sırasına değiştirmek için), satır içi karşılaştırıcıdaki sırayı değiştirmeniz veya şu şekilde kullanmanız yeterlidir reversed:

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

Ayrıca şunları da kullanabiliriz Collections.reverseOrder:

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

Böylece Collections.reverseOrder, özel nesneler için yararlı olabilecek karşılaştırıcı almak için aşırı yüklendiğini görebiliriz . reversedAslında kullanır Collections.reverseOrder:

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

teklif () ve ekle ()

Doc başına

Teklif yöntemi mümkünse bir öğe ekler, aksi takdirde false değerini döndürür. Bu, yalnızca denetlenmeyen bir özel durum atarak öğe ekleyemeyen Collection.add yönteminden farklıdır. Teklif yöntemi, örneğin sabit kapasiteli (veya "sınırlı") kuyruklarda, olağandışı bir durumdan ziyade hata normal olduğunda kullanılmak üzere tasarlanmıştır.

Kapasite kısıtlı bir kuyruk kullanıldığında, offer () genellikle add () için tercih edilir, bu da sadece bir istisna atarak bir eleman ekleyemez. Ve PriorityQueue öncelik yığın dayalı bir sınırsız öncelikli kuyruk olduğunu.


Ben 5kuyruk başlangıç ​​kapasitesini gösterir varsayalım?
Neil

1
@Neil Evet, şimdi cevabı daha açık yaptım :)
akhil_mittal

1
Java'nın 8. sürümü, dile gelen en iyi şeydi
GabrielBB

1
Berrak örnekle çok güzel bir açıklama.
Vishwa Ratna

24

Sadece uygun düzgün geçmesi Comparatoriçin yapıcı :

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

Arasındaki tek fark offerve addait oldukları arayüzüdür. offeraittir Queue<E>, oysa addbaşlangıçta görülen Collection<E>arayüz. Bunun dışında her iki yöntem de aynı şeyi yapar - belirtilen öğeyi öncelik sırasına ekleyin.


7
Kapasite kısıtlamaları, öğenin kuyruğa eklenmesini önlerse, teklif yanlış döndürürse add () bir istisna atar. PriorityQueues maksimum kapasiteye sahip olmadığından, fark tartışmalıdır.
James

Bu, add () ve offer () arasında çok açık bir ayrımdır. Ve add () zaten uygulanması gerekiyordu!
whitehat

19

dan Kuyruk API :

Teklif yöntemi mümkünse bir öğe ekler, aksi takdirde false değerini döndürür. Bu, yalnızca denetlenmeyen bir özel durum atarak öğe ekleyemeyen Collection.add yönteminden farklıdır. Teklif yöntemi, örneğin sabit kapasiteli (veya "sınırlı") kuyruklarda, olağandışı bir durumdan ziyade hata normal olduğunda kullanılmak üzere tasarlanmıştır.


12

farklı değil, javadoc'ta beyan edildiği gibi:

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

6

Sadece add()vs offer()sorusunu cevaplamak için (diğeri imo'ya mükemmel bir şekilde cevaplandığından ve bu olmayabilir):

Göre Sırasına arayüzü üzerinde JavaDoc'u , "teklif yöntemi aksi takdirde yanlış dönen bir eleman mümkünse ekler. Sadece işaretlenmemiş durum atarak bir eleman eklemek için başarısız olabilir Collection.add yönteminden farklı olması. Teklif yöntemi için tasarlanmış başarısızlık, olağan dışı bir durumdan ziyade normal bir durum olduğunda, örneğin sabit kapasiteli (veya "sınırlı") kuyruklarda kullanılır. "

Bu, öğeyi ekleyebileceğiniz anlamına gelir (bir PriorityQueue'da her zaman böyle olmalıdır), tam olarak aynı şekilde çalışırlar. Ancak öğeyi ekleyemezseniz, kodunuzda istemediğiniz kötü bir denetlenmeyen istisna atarken, offer()size güzel ve güzel bir falsedönüş sağlar add(). Eklenememesi, kodun amaçlandığı gibi çalıştığı anlamına gelirse ve / veya normal olarak kontrol edeceğiniz bir şeyse kullanın offer(). Eklenememesi bir şeyin bozulduğu anlamına gelirse , Koleksiyon arayüzünün özelliklerineadd() göre atılan istisnayı kullanın ve işleyin .

Her ikisi de, bir offer()başarısızlık false( kapasite kısıtlı kuyruklarda tercih edilen yöntem) döndürerek başarısızlıkları belirten Kuyruk arabirimindeki sözleşmeyi yerine getirmek ve ayrıca bir istisna atarak her zaman başarısız olduğunu belirtenadd() sözleşmeyi korumak için bu şekilde uygulanır .

Her neyse, umarım bu sorunun en azından bir kısmını aydınlatır.


6

Burada, kullanıcı tanımlı karşılaştırıcıyı tanımlayabiliriz:

Aşağıdaki kod:

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

Çıktı :

   india                                               
   pakistan                                         
   bangladesh

Teklif ve ekleme yöntemleri arasındaki fark: bağlantı


1
eşit olursa.
nycynik

4

Geçin a Comparator. Yerine istediğiniz türü doldurunT

Lambdas kullanma (Java 8+):

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

Anonim sınıf kullanarak klasik yol:

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

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

});

Ters sırada sıralamak için, sadece e1, e2'yi değiştirin.


3

Baskı siparişini de merak ediyordum. Bu durumu düşünün, örneğin:

Öncelik kuyruğu için:

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

Bu kod:

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

aşağıdakilerden farklı baskı yapabilir:

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

Bir kullanıcının "offer () / add () yöntemleri yalnızca öğeyi kuyruğa eklediğini söylediği başka bir forumdaki tartışmanın cevabını buldum . Tahmin edilebilir bir sipariş istiyorsanız, başını döndüren peek / anket kullanmalısınız ".


3

Kullanmaya alternatif olarak Comparator, PriorityQueue uygulamanızdaComparable kullandığınız sınıfı da kullanabilirsiniz (ve buna karşılık olarak compareToyöntemi geçersiz kılabilirsiniz ).

Genellikle , bu sıralamanın nesnenin sezgisel sıralaması olması Comparableyerine kullanılması en iyisidir Comparator- örneğin, Personnesneleri yaşa göre sıralamak için bir kullanım durumunuz varsa, muhtemelen Comparatorbunun yerine kullanmak en iyisidir .

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

Çıktı:

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

1

Öncelik Kuyruğu'nun her bir öğeye atanmış bir önceliği vardır; En yüksek önceliğe sahip öğe Sıranın Üstünde görünür. Şimdi, önceliğin her bir öğeye nasıl atanmasını istediğinize bağlı. Bunu yapmazsanız, Java bunu varsayılan şekilde yapar. En düşük değere sahip öğeye en yüksek öncelik atanır ve bu nedenle önce kuyruktan kaldırılır. Aynı en yüksek önceliğe sahip birkaç eleman varsa, kravat keyfi olarak kesilir. Ayrıca yapıcıdaki Karşılaştırıcı'yı kullanarak bir sipariş belirtebilirsiniz PriorityQueue(initialCapacity, comparator)

Örnek Kod:

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() + " ");
}

Çıktı:

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

Else, Özel Karşılaştırıcıyı da tanımlayabilirsiniz:

import java.util.Comparator;

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

1

İlk öğrenme için kullanabileceğiniz basit örnek:

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

}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.