1. Введение
В этом руководстве мы обсудим интерфейс очереди Java .
Во- первых, мы будем принимать быстрый взгляд на то , что очередь делает, и некоторые из его основных методов . Далее мы углубимся в ряд реализаций, которые Java предоставляет в качестве стандарта.
Наконец, мы поговорим о безопасности потоков, прежде чем подвести итоги.
2. Визуализация очереди
Начнем с быстрой аналогии.
Представьте, что мы только что открыли наш первый бизнес - киоск с хот-догами. Мы хотим обслуживать наших новых потенциальных клиентов наиболее эффективным способом для нашего малого бизнеса; один за раз. Во-первых, мы просим их выстроиться в упорядоченную очередь перед нашим стендом, а новые клиенты присоединятся к задней части. Благодаря нашим организационным навыкам теперь мы можем честно распределить наши вкусные хот-доги.
Очереди в Java работают аналогичным образом. После того, как мы объявим нашу очередь, мы можем добавить новые элементы сзади и удалить их спереди.
Фактически, большинство очередей, с которыми мы встретимся в Java, работают по принципу «первым пришел - первым обслужен» - часто сокращенно FIFO.
Однако есть одно исключение, о котором мы поговорим позже.
3. Основные методы
В Queue объявляется ряд методов, которые должны быть закодированы всеми реализующими классами. Давайте теперь обрисуем несколько наиболее важных из них:
- offer () - вставляет новый элемент в очередь
- poll () - удаляет элемент из начала очереди
- peek () - проверяет элемент в начале очереди, не удаляя его
4. AbstractQueue
AbstractQueue - это простейшая возможная реализация очереди , предоставляемая Java. Он включает в себя скелетную реализацию некоторых методов интерфейса Queue , за исключением предложения .
Когда мы создаем пользовательскую очередь , расширяющую AbstractQueue класса , мы должны обеспечить реализацию этого предложения методы , который никак не позволяет вставке элементов нуля.
Кроме того, мы должны обеспечить методы выглядывать, опрос, размер, и java.util «S итератор .
Давайте соберем простую реализацию очереди с использованием AbstractQueue.
Во-первых, давайте определим наш класс с помощью LinkedList для хранения элементов нашей очереди :
public class CustomBaeldungQueue extends AbstractQueue { private LinkedList elements; public CustomBaeldungQueue() { this.elements = new LinkedList(); } }
Затем давайте переопределим необходимые методы и предоставим код:
@Override public Iterator iterator() { return elements.iterator(); } @Override public int size() { return elements.size(); } @Override public boolean offer(T t) { if(t == null) return false; elements.add(t); return true; } @Override public T poll() { Iterator iter = elements.iterator(); T t = iter.next(); if(t != null){ iter.remove(); return t; } return null; } @Override public T peek() { return elements.getFirst(); }
Отлично, давайте проверим, что он работает, с помощью быстрого модульного теста:
customQueue.add(7); customQueue.add(5); int first = customQueue.poll(); int second = customQueue.poll(); assertEquals(7, first); assertEquals(5, second);
4. Суб-интерфейсы
Как правило, интерфейс очереди наследуется тремя основными подчиненными интерфейсами. Блокирующие очереди, очереди передачи и Deques .
Вместе эти 3 интерфейса реализуются подавляющим большинством доступных очередей Java . Давайте кратко рассмотрим, для чего предназначены эти интерфейсы.
4.1. Блокировка очередей
BlockingQueue интерфейс поддерживает дополнительные операции , которые заставляют нить ждать на очередь в зависимости от текущего состояния. Поток может ждать, пока Очередь не станет пустой при попытке извлечения, или пока она не станет пустой при добавлении нового элемента.
Стандартные очереди блокировки включают LinkedBlockingQueue, SynchronousQueue и ArrayBlockingQueue .
Для получения дополнительной информации перейдите к нашей статье о блокировании очередей .
4.2. Очереди передачи
The TransferQueue interface extends the BlockingQueue interface but is tailored toward the producer-consumer pattern. It controls the flow of information from producer to consumer, creating backpressure in the system.
Java ships with one implementation of the TransferQueue interface, LinkedTransferQueue.
4.3. Deques
Deque is short for Double-Ended Queue and is analogous to a deck of cards – elements may be taken from both the start and end of the Deque. Much like the traditional Queue, the Deque provides methods to add, retrieve and peek at elements held at both the top and bottom.
For a detailed guide on how the Deque works, check out our ArrayDeque article.
5. Priority Queues
We saw earlier that most of the Queues that we come across in Java follow the FIFO principle.
One such exception to this rule is the PriorityQueue. When new elements are inserted into the PriorityQueue, they are ordered based on their natural ordering, or by a defined Comparator provided when we construct the PriorityQueue.
Let's take a look at how this works with a simple unit test:
PriorityQueue integerQueue = new PriorityQueue(); integerQueue.add(9); integerQueue.add(2); integerQueue.add(4); int first = integerQueue.poll(); int second = integerQueue.poll(); int third = integerQueue.poll(); assertEquals(2, first); assertEquals(4, second); assertEquals(9, third);
Despite the order in which our integers were added to the PriorityQueue, we can see that the retrieval order is changed according to the natural order of the numbers.
We can see that the same is also true when applied to Strings:
PriorityQueue stringQueue = new PriorityQueue(); stringQueue.add("blueberry"); stringQueue.add("apple"); stringQueue.add("cherry"); String first = stringQueue.poll(); String second = stringQueue.poll(); String third = stringQueue.poll(); assertEquals("apple", first); assertEquals("blueberry", second); assertEquals("cherry", third);
6. Thread Safety
Adding items to Queues is particularly useful in multi-threaded environments. A Queue can be shared amongst threads, and be used to block progress until space is available – helping us overcome some common multi-threaded problems.
For example, writing to a single disk from multiple threads creates resource contention and can lead to slow writing times. Creating a single writer thread with a BlockingQueue can alleviate this issue and lead to vastly improved write speeds.
Luckily, Java offers ConcurrentLinkedQueue, ArrayBlockingQueue, and ConcurrentLinkedDeque which are thread-safe and perfect for multi-threaded programs.
7. Conclusion
В этом руководстве мы глубоко погрузились в интерфейс Java Queue .
Во - первых, мы исследовали то , что очередь делает , а также реализации, Java предоставляет.
Затем мы рассмотрели обычный принцип FIFO очереди , а также PriorityQueue, который отличается своим порядком.
Наконец, мы изучили безопасность потоков и то, как очереди могут использоваться в многопоточной среде.
Как всегда, код доступен на GitHub.