Главная > Java сниппеты > Двусторонние очереди (DEQUES)

Тема Зацепин
268

Java-разработчик 🧩
64
1 минуту

Двусторонние очереди (DEQUES)

Очередь поддерживает добавление элементов с одного конца и извлечение их с другого. Для сравнения двусторонняя очередь поддерживает добавление и удаление элементов с обоих концов. Ее работа напоминаю комбинацию стека и очереди. Интерфейс Deque, наследуется от интерфейса Queue, представленного в J2SE 5.0, и является одной из последних функций добавленных в Java SE 6 (Окончательное включение данной функциональности находится на рассмотрении в JCP). Данный интерфейс реализован в классах LinkedList, ArrayDeque и многопоточном классе LinkedBlockingDeque.

Добавлено : 22 Dec 2008, 19:01

Двусторонние очереди (DEQUES)

http://java.sun.com/developer/JDCTechTips/2005/tt1214.html#2

Класс LinkedList является наиболее используемым при работе с двусторонними очередями. Он не имеет ограничений на количество элементов и реализует быстродействующие методы добавления и удаления элементов с обоих концов. Класс ArrayDeque является другой реализацией не имеющей ограничения на число элементов. В нем реализована обертка для работы с индексами, обеспечивающая высокую производительность. Как и все реализации коллекций, данные реализации также не являются защищенными от многопоточного доступа. (Исторически такие коллекции как Vector и Hashtable являются защищенными от многопоточного доступа, но они не ориентированы на высокую производительность). Еслм вам нужна безопасная работа с потоками, используйте класс LinkedBlockingDeque. Класс LinkedBlockingDeque реализует интерфейс BlockingDeque наследуемый от интерфейса Deque. Данным класс может иметь ограничение на количество элементов. Если ограничение не задано, то оно равно значению Integer.MAX_VALUE.

Вы можете добавлять элементы в двустороннюю очередь при помощи следующих методов:

  • void addFirst(E e)
  • void addLast(E e)
  • boolean add(E e)

Метод add() аналогичен методу addLast(). В случае нехватки места в двусторонней очереди выбрасывается исключение IllegalStateException. Вы также можете добавить элементы при помощи следующих методов:

  • boolean offer(E e)
  • boolean offerFirst(E e)
  • boolean offerLast(E e)

В отличие от добавления элементов при помощи метода addXXX(), при добавлении элементов при помощи метода offerXXX(), возвращается значение false, если элемент не может быть добавлен.

Также существует пара методов для удаления элементов:

  • remove(), removeFirst(), and removeLast()
  • poll(), pollFirst(), and pollLast()

Методы removeXXX() выбрасывают исключение NoSuchElementException, если двусторонняя очередь пуста. Методы pollXXX() возвращают значение null, если двусторонняя очередь пуста. Вы даже можете удалять определенный объект при помощи следующих методов, хотя работа с двусторонними очередями подразумевает удаление элементов только с концов очереди.

  • boolean remove(Object o)
  • boolean removeFirstOccurrence(Object o)
  • boolean removeLastOccurrence(Object o)

В классе Deque имеется шесть методов для работы с элементами:

  • element()
  • getFirst()
  • getLast()
  • peek()
  • peekFirst()
  • peekLast()

Метод get() не определен, так как метод element() является методом интерфейса, унаследованным из интерфейса Queue. Методы get аналогичным методам removeXXX() и выбрасывают исключение NoSuchElementException, если двусторонняя очередь пуста. Методы peek в этом случае возвращают значение null. Естественно, так как вы можете добавлять элементы null, вы не можете проследить различие между элементом null и пустой двусторонней очередью. В данном случае может пригодится применение метода size().

Так как концептуально двусторонняя очередь является привязанной с двух сторон, то вы можете проводить поиск элементов в любом порядке. Используйте метод iterator() для поиска элементов с начала до конца и метод descendingIterator() для поиска элементов в обратном направлении с конца до начала. Однако вы не можете получить доступ к элементу по его местоположению, по крайней мере данный механизм не включен в интерфейс Deque. Однако класс LinkedList, реализующий интерфейс Deque, позволяет получить доступ в произвольному элементу через интерфейс List, который он также реализует. Без выполнения требования по произвольному доступу, реализация интерфейса Deque, может быть выполнена более эффективно.

Для чего использовать двусторонние очереди? Двусторонние очереди предоставляют удобные структуры данных для использования в рекурсивных процедурах, как, например работа с лабиринтами и разбор исходных данных. Когда вы разбираете исходную часть, вы сохраняете правильные варианты, добавляя с одной стороны. Если вариант оказывается не верным вы удаляете его, возвращаясь к последнему верному элементу. В данном варианте вы работаете только с одним концом, как в стеке. Как только вы достигли конца, вам необходимо вернуться в начало для получения решения, которое начинается с последнего элемента. Другим типичным примером является планировщик заданий в операционной системе.

Нижеприведенная программа Blocked, демонстрирует использование интерфейса Deque, а вернее класса LinkedBlockingDeque с установленными границами очереди. Конечно, это не лучший пример использования двусторонней очереди, но он позволяет показать применение API и события, возникающие при достижении предела очереди. Данная программа получает 23 названия месяцев (полные и сокращенные) и по одному добавляет их в начало очереди, поддерживающей шесть элементов. Другой поток удаляет элементы из начла и конца двусторонней очереди, основываясь на количестве элементов, находящихся в коллекции.

import java.io.*;
import java.util.*;
import java.util.concurrent.*;

public class Blocked {
public static void main(String args[]) {
Calendar now = Calendar.getInstance();
Locale locale = Locale.getDefault
();
final Console console = System.console();
final Map names = now.getDisplayNames(Calendar.MONTH,
Calendar.ALL_STYLES, locale
);
console.printf
("Starting names: %s%n", names);
final Deque deque = new LinkedBlockingDeque(6);
// Добавление элемента в начало
new Thread() {
public void run() {
Set keys = names.keySet();
Iterator itor = keys.iterator
();
String element =
null;
while (itor.hasNext() || element != null) {
if (element == null) {
element = itor.next();
console.printf
("MapGot: %s%n", element);
}
console.printf("Offering: %s%n", element);
if (deque.offerFirst(element)) {
console.printf("MapRemoving: %s%n", element);
itor.remove
();
element =
null;
} else {
try {
Thread.sleep(250);
} catch (InterruptedException ignored) {
}
}
}
try {
Thread.sleep(3500);
} catch (InterruptedException ignored) {
}
System.exit(0);
}
}
.start();
while (true) {
if ((deque.size() % 2 == 1)) {
// удаление из начала
console.printf("Remove head: %s%n", deque.pollFirst());
} else {
// удаление из конца
console.printf("Remove tail: %s%n", deque.pollLast());
}
// пауза между циклами
try {
Thread.sleep(500);
} catch (InterruptedException ignored) {
}
}
}
}

Как показано ниже, при выполнении программы, выводится множество строк. Это происходит из-за частого использования оператора printf(). Строка выводится на экран каждый раз, когда элемент получается из источника, удаляется из источника, добавляется в двустороннюю очередь или удаляется из нее. Обратите внимание на случай, когда добавление происходит в полную двустороннюю очередь.

>> java Blocked Starting names: {Jun=5, March=2, December=11, April=3, November=10, September=8, October=9, Sep=8, Aug=7, Apr=3, May=4, June=5, Feb=1, Dec=11, Oct=9, Jan=0, Mar=2, Jul=6, August=7, January=0, February=1, July=6, Nov=10} Remove tail: null MapGot: Jun Offering: Jun MapRemoving: Jun MapGot: March Offering: March MapRemoving: March ... Remove tail: Jul Remove head: Nov Remove tail: August Remove head: July Remove tail: January Remove head: February Remove tail: null

Следует отметить две вещи. Первое, используется только 23 элемента (а не 24) для представления полных и сокращенных названий месяцев, тат как "May" используется для обоих представлений. Метод getDisplayNames() возвращает таблицу, таким образом значение "May" не может быть ключом для двух полей, полного и сокращенного. Второе, если единственное, что вам необходимо - это добавление элементов с одного конца и извлечение их с другого используйте реализацию интерфейса Queue, вместо Deque.

Для сравнения двусторонних очередей и векторов см.статью An In-Depth Study of the STLDeque Container. В ней обсуждаются вопросы производительности каждого из решений. Реальные цифры для Java платформы будут другими, но основные идеи останутся верными.

Теги: deques

Еще от автора

Применение WeakHashmap для списков слушателей

В статье от 11мая 1999 года Reference Objects были описаны основные идеи применения ссылочных объектов, но не приводилось детального описания. Данная статья позволит вам получить больше сведений, касающихся данной темы. В основном ссылочные объекты применяются для косвенных ссылок на память необходимую объектам. Ссылочные объекты хранятся в очереди (класс ReferenceQueue), в которой отслеживается доступность ссылочных объектов. Исходя из типа ссылочного объекта, сборщик мусора может освобождать память даже тогда, когда обычные ссылки не могут быть освобождены.

Заставки в Mustang

Согласно определению, данному в Wikipedia, заставка - это компьютерный термин, обозначающий рисунок, появляющийся во время загрузки программы или операционной системы. Заставка для пользователя является визуальным отображением инициализации программы. До выхода версии Java SE 6 (кодовое название Mustang) единственной возможностью применения заставки было создание окна, во время запуска метода main, и размещение в нем картинки. Хотя данный способ и работал, но он требовал полной инициализации исполняемой Java среды до появления окна заставки. При инициализации загружались библиотеки AWT и Swing, таким образом, появление заставки задерживалось. В Mustang появился новый аргумент командной строки, значительно облегчающий использование заставок. Этот способ позволяет выводить заставку значительно быстрее до запуска исполняемой Java среды. Окончательное добавление данной функциональности находится на рассмотрении в JCP.

Совмещение изображений

1 Введение 2 Правила визуализации и пример 3 Совмещение изображений в оперативной памяти 4 Постепенное исчезновение изображения 5 Ссылки и дополнительная информация

Еще по теме

Применение WeakHashmap для списков слушателей

В статье от 11мая 1999 года Reference Objects были описаны основные идеи применения ссылочных объектов, но не приводилось детального описания. Данная статья позволит вам получить больше сведений, касающихся данной темы. В основном ссылочные объекты применяются для косвенных ссылок на память необходимую объектам. Ссылочные объекты хранятся в очереди (класс ReferenceQueue), в которой отслеживается доступность ссылочных объектов. Исходя из типа ссылочного объекта, сборщик мусора может освобождать память даже тогда, когда обычные ссылки не могут быть освобождены.

Заставки в Mustang

Согласно определению, данному в Wikipedia, заставка - это компьютерный термин, обозначающий рисунок, появляющийся во время загрузки программы или операционной системы. Заставка для пользователя является визуальным отображением инициализации программы. До выхода версии Java SE 6 (кодовое название Mustang) единственной возможностью применения заставки было создание окна, во время запуска метода main, и размещение в нем картинки. Хотя данный способ и работал, но он требовал полной инициализации исполняемой Java среды до появления окна заставки. При инициализации загружались библиотеки AWT и Swing, таким образом, появление заставки задерживалось. В Mustang появился новый аргумент командной строки, значительно облегчающий использование заставок. Этот способ позволяет выводить заставку значительно быстрее до запуска исполняемой Java среды. Окончательное добавление данной функциональности находится на рассмотрении в JCP.

Использование потоков

1 Введение 2 Работа с выражениями типа Boolean 3 Класс JoptionPane 4 Приложение-счетчик 5 Ссылки

Перехват необрабатываемых исключений

В статье от 16 марта 2004 года Best Practices in Exception Handling были описаны приемы обработки исключений. В данной статье вы изучите новый способ обработки исключений при помощи класса UncaughtExceptionHandler добавленного в J2SE 5.0.

Использование класса LinkedHashMap

1 Введение 2 Сортировка хэш-таблицы 3 Копирование таблицы 4 Сохранение порядка доступа к элементам 5 Ссылки

Сказ про кодировки и java

С кодировками в java плохо. Т.е., наоборот, все идеально хорошо: внутреннее представление строк – Utf16-BE (и поддержка Unicode была с самых первых дней). Все возможные функции умеют преобразовывать строку из маленького регистра в большой, проверять является ли данный символ буквой или цифрой, выполнять поиск в строке (в том числе с регулярными выражениями) и прочее и прочее. Для этих операций не нужно использовать какие-то посторонние библиотеки вроде привычных для php mbstring или iconv. Как говорится, поддержка многоязычных тестов “есть в коробке”. Так откуда берутся проблемы? Проблемы возникают, как только строки текста пытаются “выбраться” из jvm (операции вывода текста различным потребителям) или наоборот пытаются в эту самую jvm “залезть” (операция чтения данных от некоторого поставщика).