Як мені користуватися черговістю пріоритетів?


Відповіді:


449

Використовуйте перевантаження конструктора, яке приймає 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
Хм ... щойно помічено ... priorQueue.comparator () "Повертає компаратор, який використовується для замовлення цієї колекції, або нульовий, якщо ця колекція відсортована за природними впорядкованими елементами (використовуючи Порівнянні)." Чи означає це, що я міг би просто реалізувати порівнянне на своєму класі?
Свиш

7
Можна, так. Я б цього не робив, якщо для вашого класу не існує єдиного порядку природного сортування. Якщо є, це правильно зробити :)
Джон Скіт

8
Чи не має бути compareвпровадження просто return x.length() - y.length()? (Уникає передбачення гілок)
Френкі

7
@Franky: Можливо, так - хоча я б сказав, що це трохи важче зрозуміти, і мета відповіді - продемонструвати, як це працює. Я додаю коментар, хоча.
Джон Скіт

2
@KarelG: Я не думаю, що це має велике значення, якщо ви знаєте про відмінності. Я думаю, якщо ти використовуєш add()для операції додавання, то remove()почуваєш себе розумним; якби я користувався, offer()я, мабуть, використовував би poll()... але це лише особисті переваги.
Джон Скіт

68

Рішення Java 8

Ми можемо використовувати lambda expressionабо method referenceвводити в Java 8. Якщо у нас є деякі значення String, що зберігаються в черзі пріоритетів (ємністю 5), ми можемо надати вбудований компаратор (виходячи з довжини 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вказує на стартову ємність черги?
Ніл

1
@Neil Так, я зробив це більш чітко у відповіді зараз :)
akhil_mittal

1
Восьма версія Java була найкращою річчю з мовою
GabrielBB

1
Дуже приємне пояснення з чітким прикладом.
Vishwa Ratna

24

Просто пройти відповідну Comparatorдо конструктору :

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

Єдина відмінність між ними offerта addінтерфейсом, який вони належать offerналежить Queue<E>, тоді addяк спочатку розглядається в Collection<E>інтерфейсі. Крім того, що обидва способи роблять абсолютно одне і те ж - вставляйте зазначений елемент у чергу пріоритетів.


7
Зокрема, додавання () кидає виняток, якщо обмеження ємності перешкоджають доданню елемента до черги, тоді як пропозиція повертає помилкові. Оскільки PriorityQueues не мають максимальної ємності, різниця - суперечлива.
Джеймс

Це дуже чітке розмежування між add () і пропозицією () .. І add () потрібно було впровадити в будь-якому випадку!
whitehat

19

від API черги :

Метод пропозиції, якщо можливо, вставляє елемент, інакше повертає помилку. Це відрізняється від методу Collection.add, який не може додати елемент лише шляхом викидання неперевіреного винятку. Метод пропозиції призначений для використання, коли вихід з ладу є звичайним, а не винятковим явищем, наприклад, у чергах з фіксованою ємністю (або "обмеженими").


12

не відрізняється, як заявляють у javadoc:

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

6

Просто щоб відповісти на запитання add()vs offer()(оскільки на інший ідеально відповідає imo, а це може бути не так):

Згідно з JavaDoc на черзі інтерфейсу , "Метод пропозиції, якщо це можливо, вставляє елемент, інакше повертає помилку. Це відрізняється від методу Collection.add, який не може додати елемент лише шляхом викидання неперевіреного винятку. Метод пропозиції призначений для використання, коли збій - це звичайне, а не виняткове явище, наприклад, у чергах з фіксованою ємністю (або "обмеженою") ".

Це означає, що якщо ви можете додати елемент (який завжди має бути у черзі PriorityQueue), вони працюють точно так само. Але якщо ви не можете додати елемент, offer()ви отримаєте хороший і гарний falseприбуток, при цьому add()викидає неприємний неперевірений виняток, який ви не хочете у своєму коді. Якщо додавання коду означає, що код працює за призначенням та / або ви перевірите це нормально, скористайтеся offer(). Якщо неможливість додати означає, що щось порушено, використовуйте add()та обробляйте отриманий виняток, кинутий відповідно до специфікацій інтерфейсу колекції .

Вони обидва реалізовані таким чином для повного заповнення договору на інтерфейсі черги, який визначає 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

Використання лямбдів (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);

Я знайшов відповідь з обговорення на іншому форумі , де користувач сказав: "Методи пропозиції () / додати () лише вставляють елемент у чергу. Якщо ви хочете передбачити передбачуване замовлення, слід використати заглянути / опитування, яке поверне голову" черги ".


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

Черги пріоритетів мають певний пріоритет, присвоєний кожному елементу. Елемент із найвищим пріоритетом з’являється у верхній частині черги. Тепер, від вас залежить, яким чином ви хочете, щоб пріоритет був призначений кожному з елементів. Якщо цього не зробити, Java зробить це за замовчуванням. Елементу з найменшим значенням присвоюється найвищий пріоритет і тому спочатку видаляється з черги. Якщо є кілька елементів з однаковим найвищим пріоритетом, краватку розривають довільно. Ви також можете вказати замовлення за допомогою Comparator у конструкторі 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 

Крім того, Ви також можете визначити спеціальний компаратор:

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

}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.