Почему я должен использовать Deque над Stack?

Мне нужна структура данных Stack для моего варианта использования. Я должен иметь возможность вставлять элементы в структуру данных, и я хочу только извлечь последний элемент из Stack. JavaDoc для стека говорит:

Более полный и последовательный набор операций стека LIFO предоставляемые интерфейсом Deque и его реализациями, которые должны используется для этого класса. Например:

Deque<Integer> stack = new ArrayDeque<>();

Мне определенно не нужно синхронизировать поведение, поскольку я буду использовать эту структуру данных локально для метода. Помимо этого, почему я предпочитаю Deque над Stack здесь?

P.S: Джавадок из Deque говорит:

Deques также может использоваться как стеки LIFO (Last-In-First-Out). Эта интерфейс следует использовать вместо старого класса Stack.

Ответ 1

С одной стороны, это более разумно с точки зрения наследования. Тот факт, что Stack расширяет Vector, на мой взгляд, действительно странный. В начале Java наследование было перерасходовано IMO - еще один пример - Properties.

Для меня ключевое слово в документах, которые вы цитировали, является последовательным. Deque предоставляет набор операций, которые предназначены для возможности извлечения/добавления/удаления элементов из начала или конца коллекции, итерации и т.д. - и тому подобное. Умышленно нет способа получить доступ к элементу по позиции, который предоставляет Stack поскольку он является подклассом Vector.

Да, и у Stack нет интерфейса, поэтому, если вы знаете, что вам нужны операции со Stack вы в конечном итоге фиксируете определенный конкретный класс, что обычно не является хорошей идеей.

Также, как указано в комментариях, Stack и Deque имеют обратный порядок итераций:

Stack<Integer> stack = new Stack<>();
stack.push(1);
stack.push(2);
stack.push(3);
System.out.println(new ArrayList<>(stack)); // prints 1, 2, 3


Deque<Integer> deque = new ArrayDeque<>();
deque.push(1);
deque.push(2);
deque.push(3);
System.out.println(new ArrayList<>(deque)); // prints 3, 2, 1

что также объясняется в JavaDocs для Deque.iterator():

Возвращает итератор для элементов в этой deque в правильной последовательности. Элементы будут возвращены в порядке от первого (голова) до последнего (хвост).

Ответ 2

Вот моя интерпретация несогласованности, упомянутая в описании класса Stack.

Если вы посмотрите на универсальные реализации здесь - вы увидите, что существует последовательный подход к реализации набора, карты и списка.

  • Для множества и отображения у нас есть 2 стандартных реализации с хэш-картами и деревьями. Первый используется наиболее часто, а второй используется, когда нам нужна упорядоченная структура (а также реализует собственный интерфейс - SortedSet или SortedMap).

  • Мы можем использовать предпочтительный стиль объявления типа Set<String> set = new HashSet<String>();, чтобы увидеть причины здесь.

Но класс Stack: 1) не имеет собственного интерфейса; 2) является подклассом класса Vector, который основан на изменяемом массиве; поэтому где связанная реализация списка стека?

В интерфейсе Deque у нас нет таких проблем, включая две реализации (resizable array - ArrayDeque, связанный список - LinkedList).

Ответ 3

Еще одна причина использовать Dequeue поверх стека состоит в том, что Dequeue имеет возможность использовать преобразование потоков в список с сохранением концепции LIFO, в то время как Stack этого не делает.

Stack<Integer> stack = new Stack<>();
Deque<Integer> deque = new ArrayDeque<>();

stack.push(1);//1 is the top
deque.push(1)//1 is the top
stack.push(2);//2 is the top
deque.push(2);//2 is the top

List<Integer> list1 = stack.stream().collect(Collectors.toList());//[1,2]

List<Integer> list2 = deque.stream().collect(Collectors.toList());//[2,1]

Ответ 4

Для меня эта конкретная точка отсутствовала: Stack - это Threadsafe, поскольку он получен из Vector, тогда как большинство реализаций deque не являются и, следовательно, быстрее, если вы используете его только в одном потоке.

Ответ 5

Производительность может быть причиной. Алгоритм, который я использовал, сократился с 7,6 до 1,5 минут, просто заменив Stack на Deque.

Ответ 6

Используется Deque - ситуация, когда вы хотите извлекать элементы из головы и хвоста. Если вам нужен простой стек, нет необходимости идти на деку.