スタックでDequeを使用する必要があるのはなぜですか?


157

Stackユースケースのデータ構造が必要です。アイテムをデータ構造にプッシュでき、スタックから最後のアイテムのみを取得したいのですが。StackJavaDocはこう言っています:

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

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

このデータ構造をメソッドにローカルで使用するため、ここでは同期動作は絶対に必要ありません。これとは別に、なぜ私DequeStackここより好むのですか?

PS:Dequeのjavadocによると:

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


1
それはより多くの組み込みメソッド、またはむしろそれらの「より完全で一貫性のあるセット」を提供し、それらを利用する場合に記述する必要があるコードの量を削減しますか?

回答:


190

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 for Deque.iterator()でも説明されています。

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


10
ArrayDequeのjavadocは、「このクラスはスタックとして使用する場合はStackよりも高速であり、キューとして使用する場合はLinkedListよりも高速である可能性が高い」と述べています。..これをスタックとして使用するか、キューとして使用するかを指定するにはどうすればよいですか?
オタク2012

23
@オタク:あなたはしません。ポイントは、キューイング動作が必要な場合はを使用できますがLinkedListArrayDequeue(多くの場合)高速になるということです。スタック動作が必要な場合使用できますStackが、ArrayDeque(多くの場合)高速になります。
Jon Skeet、2012

7
抽象化という点ではあまり賢明ではありませんか?Stackつまり、どちらのソリューションも抽象化の点では本当に良いものではありません。それは、担当者の露出の問題があるからです。しかし、スタックデータ構造が必要な場合は、push、pop、peekなどのメソッドを呼び出せるようにし、スタックのもう一方の端で行います。
PeteyPabPro 2014年

4
@JonSkeetは、上から下ではなく下から上に反復するため、スタックのイテレータも間違っています。stackoverflow.com/questions/16992758/...
パベル

1
@PeteyPabPro:その通りです。スタックとしてDequeueを使用しても、Stackの継承されたVectorメソッドとして、非LIFOの使用が許可されます。(カプセル化)問題と解決策の説明はここで見つけることができます:baddotrobot.com/blog/2013/01/10/stack-vs-deque
RICS

4

Stackクラスの説明で述べた矛盾の私の解釈は次のとおりです。

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

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

  • ここに示すSet<String> set = new HashSet<String>();理由のように、宣言の優先スタイルを使用できます

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

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


2

スタック経由でデキューを使用するもう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

DequeがStackよりも優れている理由はいくつかあります。

オブジェクト指向設計-継承、抽象化、クラス、およびインターフェース:Stackはクラス、Dequeはインターフェースです。拡張できるクラスは1つだけですが、Javaの単一のクラスで実装できるインターフェースの数はいくつでもかまいません(型の多重継承)。Dequeインターフェースを使用すると、具象Stackクラスとその祖先への依存がなくなり、柔軟性が向上します。たとえば、異なるクラスを拡張したり、Dequeの異なる実装(LinkedList、ArrayDequeなど)を交換したりできるようになります。

不整合:StackはVectorクラスを拡張します。これにより、インデックスで要素にアクセスできます。これは、スタックが実際に行うべきことと一致していません。そのため、Dequeインターフェースが推奨されます(そのような操作は許可されません)。許可される操作は、FIFOまたはLIFOデータ構造が許可する必要があるものと一貫しています。

パフォーマンス:Stackが拡張するVectorクラスは、基本的にはArrayListの「スレッドセーフ」バージョンです。同期により、アプリケーションのパフォーマンスが大幅に低下する可能性があります。また、(#2で説明した)不要な機能を使用して他のクラスを拡張すると、オブジェクトが肥大化し、余分なメモリとパフォーマンスのオーバーヘッドが大量に発生する可能性があります。


-1

私にとってこの特定のポイントはありませんでした:StackはVectorから派生しているためスレッドセーフですが、最もdequeな実装はそうではなく、単一スレッドでのみ使用する場合は高速です。


grep「これとは別」
パチェリエ

-1

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


grep「これとは別」
パチェリエ

-6

Dequeが使用されるのは、ヘッドとテールの両方から要素を取得する場合です。単純なスタックが必要な場合は、両端キューを取得する必要はありません。


1
質問の最後の段落をご覧ください。javadocは、Dequesを使用する必要があると言っていますが、その理由を知りたいのですが?
オタク2012

2
途中で要素にアクセスできないため、 Dequeが推奨さます。それはダブルエンドなので、FIFOのFILOにすることができます。
Thufir 2013

彼らが言おうとしているのは、クラスがStack操作を明示的なメソッドとしてサポートしているということです。ドキュメントとメソッドを参照してください。スタックの実装を明示的にサポートしています。
Abhishek Nandgaonkar
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.