Время от времени нам нужно обрабатывать элементы очереди в определенном порядке. Приоритетная очередь – это структура данных, которая справляется с этой задачей. Приоритетная очередь в Java отличается от “обычной” очереди. Вместо “первым вошел – первым вышел” она извлекает элементы в порядке их приоритета.
Приоритетная очередь в Java
Класс java.util.PriorityQueue
предоставляет нам реализацию такого типа данных, используя внутреннюю реализацию приоритетной кучи. Java PriorityQueue является неограниченной очередью. Она была введена в Java 1.5 и улучшена в релизе Java SE 8. PriorityQueue внутренне реализована с использованием структуры данных “приоритетная куча”. Вот иерархия классов PriorityQueue: Диаграмма классов PriorityQueue:
Конструкторы Java PriorityQueue
- PriorityQueue() – Создает PriorityQueue с размером по умолчанию, т.е. 11
- PriorityQueue(Collection c) – Создает PriorityQueue с элементами из указанной коллекции.
- PriorityQueue(int initialCapacity) – Создает PriorityQueue с указанной начальной емкостью
- PriorityQueue(int initialCapacity, Comparator comparator) – Создает PriorityQueue с указанной начальной емкостью и порядком элементов в соответствии с указанным компаратором
- PriorityQueue(PriorityQueue c) – Создает PriorityQueue, содержащий элементы из указанной очереди с приоритетом
- PriorityQueue(SortedSet c) – Создает PriorityQueue, содержащий элементы из указанного отсортированного набора
Элементы очереди с приоритетом упорядочены по их естественному порядку, если не предоставлен Comparator
при создании. Элементы упорядочены по возрастанию по умолчанию, поэтому головой очереди является элемент с наименьшим приоритетом. Если есть два элемента, которые имеют право стать головой одновременно, эти связи разрешаются произвольно.
Пример Java PriorityQueue
Давайте создадим PriorityQueue
, содержащую различные задачи:
PriorityQueue tasks=new PriorityQueue();
tasks.add("task1");
tasks.add("task4");
tasks.add("task3");
tasks.add("task2");
tasks.add("task5");
Это создает PriorityQueue задач, которые будут упорядочены естественным образом для String
. Давайте создадим еще одну PriorityQueue, которая упорядочивает задачи в обратном порядке естественного порядка. Для этого нам нужно передать компаратор:
PriorityQueue reverseTasks=new PriorityQueue(Comparator.reverseOrder());
reverseTasks.add("task1");
reverseTasks.add("task4");
reverseTasks.add("task3");
reverseTasks.add("task2");
reverseTasks.add("task5");
Методы PriorityQueue в Java
Теперь давайте рассмотрим все доступные методы для PriorityQueue и воспользуемся ими:
-
Boolean add(E e) – Этот метод вставляет указанный элемент в очередь. Мы уже добавили 5 задач в нашу очередь с использованием этого метода.
-
Comparator comparator() – Этот метод возвращает компаратор, используемый для упорядочения элементов в этой очереди. Он возвращает null, если компаратор не был указан, и очередь упорядочена в соответствии с естественным порядком ее элементов. Таким образом, если мы сделаем следующее:
System.out.println(tasks.comparator()); System.out.println(reverseTasks.comparator());
Вывод будет следующим:
null java.util.Collections$ReverseComparator@15db9742
-
boolean contains(Object o) – Возвращает true, если очередь содержит указанный элемент. Давайте проверим, принадлежит ли “task3” к очереди с приоритетами задач:
System.out.println(tasks.contains("task3"));
Это печатает:
true
-
boolean offer(E e) – Как и метод add(), этот метод также добавляет элемент в очередь. Методы offer() и add() фактически немного отличаются для очередей с ограниченной вместимостью, но в случае PriorityQueue они оба одинаковы. В отличие от add(), метод offer() не выбрасывает исключение, даже если не удается добавить элемент в очередь.
-
E peek() – Извлекает голову этой очереди или возвращает null, если очередь пуста. Другими словами, он возвращает элемент с наивысшим приоритетом. Так что следующий код:
System.out.println(tasks.peek()); System.out.println(reverseTasks.peek());
Дает нам:
task1 task5
-
E poll() – Этот метод также извлекает голову очереди (элемент с наивысшим приоритетом), или возвращает null, если очередь пуста. Но в отличие от peek(), он также удаляет элемент. Так что, если мы вызовем poll():
System.out.println(“Poll on tasks: ”+tasks.poll()); System.out.println(“Poll on reverseTasks: ”+reverseTasks.poll());
Затем peek:
System.out.println(“Peek on tasks: ”+tasks.peek()); System.out.println(“Peek on reverseTasks: ”+reverseTasks.peek());
У нас будет следующий вывод:
Poll on tasks: task1 Poll on reverseTasks: task5 Peek on tasks: task2 Peek on reverseTasks: task4
-
int size() – Возвращает количество элементов в очереди.
-
boolean remove(Object o) – Удаляет указанный элемент из очереди, если он присутствует. Если два одинаковых элемента присутствуют, удаляется только один из них.
-
Object[] toArray() – Возвращает массив, содержащий все элементы в очереди.
-
T[] toArray(T[] a) – Возвращает массив, содержащий все элементы в очереди, и тип возвращаемого массива совпадает с указанным массивом.
-
Iterator iterator() – Возвращает итератор для очереди.
-
void clear() – Удаляет все элементы из очереди.
Помимо этого, PriorityQueue
также наследует методы из классов Collection
и Object
.
Временная сложность Java PriorityQueue
- Для методов вставки и извлечения временная сложность составляет O(log(n))
- Для методов remove(Object) и contains(Object) временная сложность линейная
- Для методов извлечения временная сложность постоянная
Эта реализация приоритетной очереди не является потокобезопасной. Поэтому, если нам нужен синхронизированный доступ, мы должны использовать PriorityBlockingQueue. Ссылка: Документация по API
Source:
https://www.digitalocean.com/community/tutorials/priority-queue-java