web-dev-qa-db-ja.com

Deque over Stackを使用する理由は何ですか?

ユースケースにはStackデータ構造が必要です。アイテムをデータ構造にプッシュできるはずで、Stackから最後のアイテムを取得したいだけです。 StackのJavaDoc は言う:

より完全で一貫性のあるLIFOスタック操作のセットは、Dequeインターフェイスとその実装によって提供されます。これらのクラスは、このクラスよりも優先して使用する必要があります。例えば:

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

メソッドにローカルなこのデータ構造を使用するため、ここでは同期動作を絶対に望まない。これとは別に、ここでDequeよりもStackを好むのはなぜですか?

追伸:Dequeのjavadocによると:

両端キューは、LIFO(Last-In-First-Out)スタックとしても使用できます。このインターフェイスは、従来のStackクラスよりも優先して使用する必要があります。

131
Geek

一つには、継承の観点からより賢明です。私の見解では、StackVectorを拡張するという事実は本当に奇妙です。 Javaの初期では、IMOの継承が過剰に使用されていました-Propertiesは別の例です。

私にとって、引用したドキュメントの重要な言葉はconsistentです。 Dequeは、コレクションの先頭または末尾からアイテムを取得、追加、削除、反復などを行うことができる操作のセットを公開します-それだけです。 Stackが公開する位置によって要素にアクセスする方法は意図的にありませんbecauseそれはVectorのサブクラスです。

ああ、またStackにはインターフェースがありません。したがって、Stack操作が必要であることがわかっている場合、特定の具象クラスにコミットすることになりますが、これは通常良い考えではありません。

また、コメントで指摘されているように、StackDequeには逆の反復順序があります。

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

Deque.iterator() のJavaDocsでも説明されています。

この両端キュー内の要素を適切な順序で繰り返し処理する反復子を返します。要素は、最初(先頭)から最後(末尾)の順に返されます。

159
Jon Skeet

Stackクラスの説明で言及されている矛盾の私の解釈を以下に示します。

汎用実装 here を見ると、セット、マップ、リストの実装に一貫したアプローチがあることがわかります。

  • セットおよびマップには、ハッシュマップとツリーを使用した2つの標準実装があります。 1つ目は最もよく使用され、2つ目は順序付けられた構造が必要なときに使用されます(また、独自のインターフェイスであるSortedSetまたはSortedMapも実装します)。

  • Set<String> set = new HashSet<String>();see reason here のような宣言の好ましいスタイルを使用できます。

ただし、Stackクラス:1)独自のインターフェイスはありません。 2)Vectorクラスのサブクラス-サイズ変更可能な配列に基づいています。スタックのリンクリスト実装はどこにありますか?

Dequeインターフェースでは、2つの実装(サイズ変更可能な配列-ArrayDeque、リンクリスト-LinkedList)などの問題はありません。

3
irudyak

スタック上でデキューを使用するもう1つの理由は、デキューには、スタックではなくLIFOの概念を適用したままリストに変換するストリームを使用する機能があることです。

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]
2
Amer Qarabsa

私にとって、この特定の点が欠落していました。スタックはベクターから派生しているためスレッドセーフですが、ほとんどのdeque実装はそうではないため、単一スレッドでのみ使用する場合は高速です。

0
Andi

パフォーマンスが理由である可能性があります。私が使用したアルゴリズムは、StackをDequeに置き換えるだけで7.6分から1.5分に短縮されました。

0
eddy