Мне нужна Stack
структура данных для моего варианта использования. Я должен иметь возможность помещать элементы в структуру данных, и я хочу получить только последний элемент из стека. JavaDoc для стека говорит:
Более полный и согласованный набор операций стека LIFO обеспечивается интерфейсом Deque и его реализациями, которые следует использовать в предпочтении этому классу. Например:
Deque<Integer> stack = new ArrayDeque<>();
Я определенно не хочу синхронизированное поведение здесь, поскольку я буду использовать эту локальную для метода структуру данных. Кроме того , почему я предпочел Deque
более Stack
здесь?
PS: Javadoc от Deque говорит:
Deques также может использоваться в качестве стеков LIFO (Last-In-First-Out). Этот интерфейс следует использовать предпочтительнее унаследованного класса Stack.
источник
Ответы:
С одной стороны, это более разумно с точки зрения наследования. То, что
Stack
распространяетсяVector
, действительно странно, на мой взгляд. На ранних этапах Java наследование было чрезмерным использованием IMO,Properties
что является еще одним примером.Для меня ключевое слово в документах, которые вы цитировали, является последовательным .
Deque
предоставляет набор операций, которые предназначены для возможности извлечения / добавления / удаления элементов из начала или конца коллекции, итерации и т. д. - и все. Умышленно нет никакого способа получить доступ к элементу по позиции, которыйStack
выставляется, потому что это подклассVector
.Да, и также
Stack
не имеет интерфейса, поэтому, если вы знаете, что вам нужныStack
операции, вы в конечном итоге фиксируете определенный конкретный класс, что обычно не является хорошей идеей.Также, как указано в комментариях,
Stack
иDeque
имеют обратный порядок итераций:что также объясняется в JavaDocs для Deque.iterator () :
источник
LinkedList
, ноArrayDequeue
(часто) будет быстрее. Если вы хотите поведение стека, вы можете использовать,Stack
ноArrayDeque
(часто) будет быстрее.Stack
имеет проблему с представлением повторений, но если я хочу иметь структуру данных стека, я хотел бы иметь возможность вызывать такие методы, как push, pop и peek, а не вещи, требующие делать с другим концом стека.Вот моя интерпретация несоответствия, упомянутая в описании класса Stack.
Если вы посмотрите на Реализации общего назначения здесь - вы увидите, что существует последовательный подход к реализации множества, карты и списка.
Для множества и карты у нас есть 2 стандартные реализации с хэш-картами и деревьями. Первый наиболее часто используется, а второй используется, когда нам нужна упорядоченная структура (и он также реализует свой собственный интерфейс - SortedSet или SortedMap).
Мы можем использовать предпочтительный стиль объявления, как
Set<String> set = new HashSet<String>();
показано здесь .Но класс Stack: 1) не имеет собственного интерфейса; 2) является подклассом класса Vector, который основан на массиве изменяемого размера; Так где же реализация связанного списка стека?
В интерфейсе Deque у нас нет таких проблем, включая две реализации (изменяемый размер массива - ArrayDeque; связанный список - LinkedList).
источник
Еще одна причина использовать Dequeue поверх стека состоит в том, что Dequeue имеет возможность использовать преобразование потоков в список с сохранением концепции LIFO, в то время как Stack этого не делает.
источник
Вот несколько причин, почему Deque лучше, чем стек:
Объектно-ориентированный дизайн - наследование, абстракция, классы и интерфейсы: стек - это класс, Deque - это интерфейс. Только один класс может быть расширен, тогда как любое количество интерфейсов может быть реализовано одним классом в Java (множественное наследование типа). Использование интерфейса Deque удаляет зависимость от конкретного класса Stack и его предков и дает вам больше гибкости, например, свободу расширять другой класс или менять различные реализации Deque (например, LinkedList, ArrayDeque).
Несоответствие: стек расширяет класс Vector, что позволяет получить доступ к элементу по индексу. Это несовместимо с тем, что на самом деле должен делать стек, именно поэтому предпочтительным является интерфейс Deque (он не разрешает такие операции) - его разрешенные операции соответствуют тому, что должна разрешать структура данных FIFO или LIFO.
Производительность: класс Vector, который расширяет Stack, по сути является «потокобезопасной» версией ArrayList. Синхронизация может привести к значительному снижению производительности вашего приложения. Кроме того, расширение других классов ненужной функциональностью (как упомянуто в # 2) увеличивает объем ваших объектов, что может потребовать значительных дополнительных затрат памяти и производительности.
источник
Для меня этот конкретный момент упущен: стек является Threadsafe, поскольку он является производным от Vector, тогда как большинство реализаций deque нет, и, следовательно, быстрее, если вы используете его только в одном потоке.
источник
Производительность может быть причиной. Алгоритм, который я использовал, сократился с 7,6 до 1,5 минут, просто заменив Stack на Deque.
источник
Deque используется в ситуации, когда вы хотите получить элементы как из головы, так и из хвоста. Если вы хотите простой стек, вам не нужно идти на дек.
источник