Как мне использовать PriorityQueue?

Ответы:

450

Используйте перегрузку конструктора, которая принимает 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;
    }
}

Вот вывод:

короткая

средний

очень долго

Джон Скит
источник
8
Хм ... только что заметил ... priorityQueue.comparator () "Возвращает компаратор, использованный для упорядочения этой коллекции, или ноль, если эта коллекция отсортирована в соответствии с естественным упорядочением ее элементов (с использованием Comparable)." Означает ли это, что я мог бы просто реализовать Comparable в своем классе?
Свиш
7
Вы могли бы, да. Я бы не стал этого делать, если бы не было единственного естественного порядка сортировки для вашего класса. Если есть, то это правильно делать :)
Джон Скит
8
Разве compareреализация не должна быть просто return x.length() - y.length()? (Предотвращение ветвления прогноза)
Фрэнки
7
@Franky: Да, может быть, хотя я бы сказал, что это немного сложнее понять, и цель ответа - показать, как это работает. Я добавлю комментарий, хотя.
Джон Скит
2
@KarelG: я не думаю, что это имеет большое значение, если вы знаете о различиях. Я думаю, что если вы используете add()для операции добавления, то remove()чувствует себя разумно; если бы я использовал, offer()я бы, вероятно, использовал poll()... но это просто личное предпочтение.
Джон Скит
69

Решение Java 8

Мы можем использовать lambda expressionили method referenceвнедрить в Java 8. Если у нас есть некоторые значения String, хранящиеся в очереди приоритетов (с емкостью 5), мы можем предоставить встроенный компаратор (на основе длины String):

Использование лямбда-выражения

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

Использование метода Reference

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

предложение () против добавления ()

Согласно документу

Метод offer вставляет элемент, если это возможно, в противном случае возвращает false. Это отличается от метода Collection.add, который может не добавить элемент, только выбрасывая непроверенное исключение. Предложенный метод предназначен для использования, когда сбой является нормальным, а не исключительным случаем, например, в очередях с фиксированной (или «ограниченной») емкостью.

При использовании очереди с ограниченной емкостью, предложение (), как правило, предпочтительнее, чем add (), который может не вставить элемент, только вызвав исключение. И PriorityQueue - это очередь с неограниченным приоритетом, основанная на куче приоритетов.

akhil_mittal
источник
1
Я предполагаю, что 5указывает начальную емкость очереди?
Нил
1
@Neil Да, я сделал это более ясным в ответе сейчас :)
akhil_mittal
1
Восьмая версия Java была лучшей вещью, которая когда-либо случалась с языком
GabrielBB
1
Очень хорошее объяснение с ярким примером.
Вишва Ратна
24

Просто пройти соответствующую Comparatorк конструктору :

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

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

стрекоза
источник
7
В частности, add () генерирует исключение, если ограничения емкости не позволяют добавить элемент в очередь, а предложение возвращает false. Поскольку PriorityQueues не имеет максимальную мощность, разница является спорной.
Джеймс
Это очень четкое различие между add () и offer (). И add () необходимо было реализовать в любом случае!
whitehat
19

из API очереди :

Метод offer вставляет элемент, если это возможно, в противном случае возвращает false. Это отличается от метода Collection.add, который может не добавить элемент, только выбрасывая непроверенное исключение. Предложенный метод предназначен для использования, когда сбой является нормальным, а не исключительным случаем, например, в очередях с фиксированной (или «ограниченной») емкостью.

Питер
источник
12

ничем не отличается, как заявляют в Javadoc:

public boolean add(E e) {
    return offer(e);
}
d1ck50n
источник
6

Просто, чтобы ответить на вопрос add()против offer()(так как другой отлично ответил imo, а это может и не быть):

Согласно JavaDoc для интерфейса Queue : «Метод offer вставляет элемент, если это возможно, в противном случае возвращает false. Это отличается от метода Collection.add, который может не добавить элемент только путем генерирования непроверенного исключения. Метод offer предназначен для использовать, когда сбой является нормальным, а не исключительным случаем, например, в очередях с фиксированной (или «ограниченной») емкостью ».

Это означает, что если вы можете добавить элемент (который всегда должен иметь место в PriorityQueue), они работают точно так же. Но если вы не можете добавить элемент, offer()вы получите приятное и симпатичное falseвозвращение, в то время как создаст add()неприятное непроверенное исключение, которое вам не нужно в вашем коде. Если неудача при добавлении означает, что код работает должным образом и / или это то, что вы обычно проверяете, используйте offer(). Если ошибка добавления означает, что что-то сломано, используйте add()и обработайте полученное исключение в соответствии со спецификациями интерфейса Collection .

Они оба реализованы таким образом, чтобы полностью заполнить контракт в интерфейсе очереди, в котором указаны offer()сбои, путем возврата false( предпочтительный метод в очередях с ограниченной емкостью ), а также поддерживать контракт на интерфейсе коллекции, который всегда указывает на add()сбои, вызывая исключение .

Во всяком случае, надеюсь, что проясняет хотя бы эту часть вопроса.

Blueriver
источник
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

Разница между предложением и методами добавления: ссылка

rashedcs
источник
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);

Я нашел ответ из обсуждения на другом форуме , где пользователь сказал: «методы offer () / add () только вставляют элемент в очередь. Если вы хотите предсказуемый порядок, вы должны использовать peek / poll, который возвращает заголовок». из очереди. "

joserey
источник
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 

Иначе, Вы также можете определить Custom Comparator:

import java.util.Comparator;

public class StringLengthComparator implements Comparator<String>
{
    @Override
    public int compare(String x, String y)
    {
        //Your Own Logic
    }
}
devDeejay
источник
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());
        }
    }

}
KayV
источник