ฉันจะใช้ PriorityQueue ได้อย่างไร


374

ฉันPriorityQueueจะจัดเรียงในสิ่งที่ฉันต้องการเรียงลำดับได้อย่างไร

นอกจากนี้ยังมีความแตกต่างระหว่างofferและaddวิธีการ?

คำตอบ:


449

ใช้ Constructor Overload ซึ่งใช้เวลาComparator<? super E> comparatorและผ่านไปในตัวเปรียบเทียบที่เปรียบเทียบในวิธีที่เหมาะสมสำหรับการเรียงลำดับของคุณ หากคุณให้ตัวอย่างว่าคุณต้องการเรียงลำดับอย่างไรเราสามารถให้โค้ดตัวอย่างบางส่วนเพื่อนำเครื่องมือเปรียบเทียบมาใช้หากคุณไม่แน่ใจ (มันค่อนข้างตรงไปตรงมาแม้ว่า)

ดังที่ได้กล่าวไว้ในที่อื่น ๆ : offerและaddเป็นเพียงวิธีการใช้งานอินเตอร์เฟสที่แตกต่างกัน ในแหล่ง JDK ฉันมี, สายadd offerแม้ว่าaddและofferมีอาจเป็นพฤติกรรมที่แตกต่างกันในเนื่องจากทั่วไปเพื่อความสามารถในการofferที่จะแสดงให้เห็นว่าค่าที่ไม่สามารถเพิ่มเนื่องจากข้อ จำกัด ของขนาดแตกต่างนี้จะไม่เกี่ยวข้องในPriorityQueueซึ่งเป็นมากมาย

นี่คือตัวอย่างของลำดับความสำคัญของการเรียงลำดับตามความยาวสตริง:

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

นี่คือผลลัพธ์:

สั้น

กลาง

นานมากแน่นอน


7
อืม ... เพิ่งสังเกตเห็นว่า ... priorityQueue.comparator () "ส่งคืนเครื่องมือเปรียบเทียบที่ใช้ในการสั่งซื้อคอลเลกชันนี้หรือเป็นโมฆะถ้าคอลเลกชันนี้เรียงตามองค์ประกอบตามลำดับตามธรรมชาติ (โดยใช้การเปรียบเทียบได้)" นั่นหมายความว่าฉันสามารถนำ Comparable มาใช้กับชั้นเรียนของฉันได้หรือไม่?
Svish

7
คุณทำได้ใช่ ฉันจะไม่ทำเช่นนั้นเว้นแต่ว่าจะมีการเรียงลำดับที่เป็นธรรมชาติสำหรับชั้นเรียนของคุณ หากมีนั่นคือสิ่งที่ถูกต้องที่จะทำ :)
จอนสกีต

8
การcompareดำเนินการไม่ควรเป็นเพียงแค่return x.length() - y.length()? (หลีกเลี่ยงการคาดคะเนสาขา)
Franky

7
@Franky: มันอาจจะใช่ - แม้ว่าผมบอกว่าเป็นเล็กน้อยยากที่จะเข้าใจและวัตถุประสงค์ของคำตอบที่จะแสดงให้เห็นถึงวิธีการทำงาน ฉันจะเพิ่มความคิดเห็น
Jon Skeet

2
@ KarelG: ฉันไม่คิดว่ามันสำคัญเกินไปตราบใดที่คุณตระหนักถึงความแตกต่าง ฉันคิดว่าถ้าคุณใช้add()สำหรับการดำเนินการเพิ่มแล้วremove()รู้สึกสมเหตุสมผล ถ้าฉันใช้offer()ฉันอาจจะใช้poll()... แต่นั่นเป็นเพียงความชอบส่วนตัว
Jon Skeet

68

โซลูชัน Java 8

เราสามารถใช้lambda expressionหรือmethod referenceแนะนำใน Java 8 ในกรณีที่เรามีค่าสตริงบางอย่างที่เก็บไว้ใน Priority Queue (มีความจุ 5) เราสามารถให้ inline comparator (ขึ้นอยู่กับความยาวของ String):

การใช้แลมบ์ดาแสดงออก

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

ใช้วิธีการอ้างอิง

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

จากนั้นเราสามารถใช้พวกเขาเป็น:

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

สิ่งนี้จะพิมพ์:

Apple
PineApple
Custard Apple

ในการกลับคำสั่งซื้อ (เพื่อเปลี่ยนเป็นคิวลำดับความสำคัญสูงสุด) เพียงแค่เปลี่ยนลำดับในตัวเปรียบเทียบแบบอินไลน์หรือใช้reversedเป็น:

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

เรายังสามารถใช้Collections.reverseOrder:

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

ดังนั้นเราจะเห็นว่าCollections.reverseOrderมีการใช้งานมากเกินไปในการเปรียบเทียบซึ่งจะเป็นประโยชน์สำหรับวัตถุที่กำหนดเอง การreversedใช้งานจริงCollections.reverseOrder:

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

ข้อเสนอ () เทียบกับเพิ่ม ()

ตามเอกสาร

วิธีการเสนอแทรกองค์ประกอบถ้าเป็นไปได้มิฉะนั้นกลับเท็จ สิ่งนี้แตกต่างจากเมธอด Collection.add ซึ่งสามารถล้มเหลวในการเพิ่มองค์ประกอบโดยการโยนข้อยกเว้นที่ไม่ได้ตรวจสอบเท่านั้น วิธีการเสนอถูกออกแบบมาเพื่อใช้เมื่อความล้มเหลวเป็นเรื่องปกติแทนที่จะเกิดขึ้นเป็นพิเศษตัวอย่างเช่นในคิวที่มีความจุคงที่ (หรือ "ขอบเขต")

เมื่อใช้คิวที่ จำกัด ความจุโดยทั่วไปข้อเสนอพิเศษ () นั้นดีกว่าการเพิ่ม () ซึ่งอาจไม่สามารถแทรกองค์ประกอบได้โดยการโยนข้อยกเว้น และPriorityQueueเป็นคิวลำดับความสำคัญที่ไม่ได้ จำกัด ตามฮีปลำดับความสำคัญ


ฉันสมมติว่า5กำลังการผลิตเริ่มต้นของคิว?
Neil

1
@ Neil ใช่ฉันได้ทำให้มันชัดเจนมากขึ้นในคำตอบตอนนี้ :)
23919

1
Java เวอร์ชัน 8 เป็นสิ่งที่ดีที่สุดที่เคยเกิดขึ้นกับภาษา
GabrielBB

1
คำอธิบายที่ดีมากพร้อมตัวอย่างชัดเจน
Vishwa Ratna

24

เพียงผ่านที่เหมาะสมComparatorกับผู้สร้าง :

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

ข้อแตกต่างระหว่างofferและaddเป็นส่วนต่อประสานที่เป็นของพวกเขาเท่านั้น offerเป็นของQueue<E>ในขณะที่addแต่เดิมจะเห็นในCollection<E>อินเตอร์เฟซ นอกเหนือจากนั้นวิธีการทั้งสองทำสิ่งเดียวกัน - แทรกองค์ประกอบที่ระบุลงในคิวลำดับความสำคัญ


7
โดยเฉพาะ add () จะส่งข้อยกเว้นหากข้อ จำกัด ด้านความสามารถป้องกันไม่ให้เพิ่มรายการลงในคิวในขณะที่ข้อเสนอพิเศษคืนค่าเท็จ เนื่องจาก PriorityQueues ไม่มีความจุสูงสุดความแตกต่างคือ moot
James

นั่นคือความแตกต่างที่ชัดเจนมากระหว่างการเพิ่ม () และข้อเสนอ () .. และการเพิ่ม () จำเป็นต้องดำเนินการต่อไป!
whitehat

19

จากQueue API :

วิธีการเสนอแทรกองค์ประกอบถ้าเป็นไปได้มิฉะนั้นกลับเท็จ สิ่งนี้แตกต่างจากเมธอด Collection.add ซึ่งสามารถล้มเหลวในการเพิ่มองค์ประกอบโดยการโยนข้อยกเว้นที่ไม่ได้ตรวจสอบเท่านั้น วิธีการเสนอถูกออกแบบมาเพื่อใช้เมื่อความล้มเหลวเป็นเรื่องปกติแทนที่จะเกิดขึ้นเป็นพิเศษตัวอย่างเช่นในคิวที่มีความจุคงที่ (หรือ "ขอบเขต")



6

เพียงเพื่อตอบคำถามadd()vs offer()(เนื่องจากอีกคำถามหนึ่งนั้นตอบคำถามได้อย่างสมบูรณ์แบบและสิ่งนี้อาจไม่ใช่):

ตามJavaDoc บน interface Queue "วิธีการเสนอแทรกองค์ประกอบถ้าเป็นไปได้มิฉะนั้นกลับเท็จนี้แตกต่างจากวิธี Collection.add ซึ่งสามารถล้มเหลวในการเพิ่มองค์ประกอบโดยการโยนข้อยกเว้นไม่ได้ตรวจสอบวิธีการเสนอถูกออกแบบมาสำหรับ ใช้เมื่อความล้มเหลวเป็นเรื่องปกติแทนที่จะเกิดขึ้นเป็นพิเศษตัวอย่างเช่นในคิวที่มีความจุคงที่ (หรือ "ขอบเขต")

ซึ่งหมายความว่าหากคุณสามารถเพิ่มองค์ประกอบ (ซึ่งควรเป็นกรณีใน PriorityQueue) สิ่งเหล่านี้จะทำงานเหมือนกันทุกประการ แต่ถ้าคุณไม่สามารถเพิ่มองค์ประกอบได้offer()จะให้falseผลตอบแทนที่ดีและน่าสนใจในขณะที่add()ส่งข้อยกเว้นที่ไม่ได้ตรวจสอบที่น่ารังเกียจซึ่งคุณไม่ต้องการในรหัสของคุณ หากล้มเหลวในการเพิ่มรหัสหมายถึงคือการทำงานตามที่ตั้งใจไว้และ / offer()หรือเป็นสิ่งที่คุณจะตรวจสอบตามปกติการใช้งาน หากล้มเหลวในการเพิ่มหมายถึงบางสิ่งบางอย่างเสียการใช้งานadd()และการจัดการกับข้อยกเว้นที่เกิดโยนไปตามข้อกำหนดของอินเตอร์เฟซคอลเลกชัน

พวกเขาทั้งสองดำเนินการด้วยวิธีนี้จะ fullfill สัญญาในอินเตอร์เฟซคิวที่ระบุoffer()ล้มเหลวโดยกลับfalse( วิธีที่แนะนำในคิวกำลังการผลิตที่ จำกัด ) และยังรักษาสัญญาในอินเตอร์เฟซการเก็บที่ระบุadd()เสมอล้มเหลวโดยการขว้างปาข้อยกเว้น

อย่างไรก็ตามหวังว่าจะชี้แจงอย่างน้อยส่วนหนึ่งของคำถาม


6

ในที่นี่เราสามารถกำหนดตัวเปรียบเทียบที่ผู้ใช้กำหนด:

รหัสด้านล่าง:

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

ผลผลิต:

   india                                               
   pakistan                                         
   bangladesh

ข้อแตกต่างระหว่างข้อเสนอและวิธีการเพิ่ม: ลิงก์


1
ถ้าพวกเขาเท่ากัน
nycynik

4

Comparatorผ่านมัน กรอกประเภทที่คุณต้องการT

ใช้ lambdas (Java 8+):

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

วิธีคลาสสิกโดยใช้คลาสที่ไม่ระบุชื่อ:

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

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

});

หากต้องการเรียงลำดับแบบย้อนกลับเพียงแค่สลับ e1, e2


3

ฉันยังสงสัยเกี่ยวกับการสั่งพิมพ์ พิจารณากรณีนี้ตัวอย่างเช่น:

สำหรับคิวลำดับความสำคัญ:

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

รหัสนี้:

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

อาจพิมพ์ต่างจาก:

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

ฉันพบคำตอบจากการสนทนาในฟอรัมอื่นซึ่งผู้ใช้กล่าวว่า "ข้อเสนอ () / เพิ่ม () วิธีการแทรกองค์ประกอบลงในคิวเท่านั้นหากคุณต้องการคำสั่งที่คาดเดาได้คุณควรใช้ peek / แบบสำรวจที่ส่งคืนหัว ของคิว "


3

เพื่อเป็นทางเลือกแทนการใช้Comparatorคุณสามารถมีคลาสที่คุณใช้ในPriorityQueue การนำไปใช้Comparable (และแทนที่compareToวิธีการ)

โปรดทราบว่าโดยทั่วไปควรใช้ให้ดีที่สุดComparableแทนการComparatorสั่งซื้อนั้นเป็นการเรียงลำดับตามวัตถุที่เข้าใจง่าย - ตัวอย่างเช่นหากคุณมีกรณีการใช้งานเพื่อเรียงลำดับPersonวัตถุตามอายุอาจเป็นการดีที่สุดที่จะใช้Comparatorแทน

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

เอาท์พุท:

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

1

Priority Queue มีลำดับความสำคัญบางส่วนที่กำหนดให้กับแต่ละองค์ประกอบองค์ประกอบที่มีลำดับความสำคัญสูงสุดจะปรากฏที่ Top Of Queue ตอนนี้มันขึ้นอยู่กับคุณว่าคุณต้องการให้ความสำคัญกับองค์ประกอบแต่ละอย่างอย่างไร หากคุณไม่ทำเช่นนั้นจาวาจะทำตามวิธีเริ่มต้น องค์ประกอบที่มีค่าน้อยที่สุดจะถูกกำหนดลำดับความสำคัญสูงสุดและจะถูกลบออกจากคิวก่อน หากมีองค์ประกอบหลายอย่างที่มีลำดับความสำคัญสูงสุดเท่ากันเนคไทม์จะถูกทำลายโดยพลการ นอกจากนี้คุณยังสามารถระบุการสั่งซื้อโดยใช้เครื่องมือเปรียบเทียบในตัวสร้าง PriorityQueue(initialCapacity, comparator)

รหัสตัวอย่าง:

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

เอาท์พุท:

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

อื่น ๆ คุณยังสามารถกำหนด Custom Comparator:

import java.util.Comparator;

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

1

นี่คือตัวอย่างง่ายๆที่คุณสามารถใช้สำหรับการเรียนรู้เริ่มต้น:

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

}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.