28

実行するたびにオブジェクトを生成するメソッドがあり、それらを取得する順序を逆にする必要があります。LIFOなので、スタックを使うのが自然だと思いました。

ただし、Javaスタックは、新しい Java 8 ストリーミング API とうまく連携していないようです。

私がこれを行う場合:

   Stack<String> stack = new Stack<String>();
   stack.push("A");
   stack.push("B");
   stack.push("C");

   List<String> list = stack.stream().collect(Collectors.toList());
   System.out.println("Collected: " + list);

私が得る出力は次のとおりです。

Collected: [A, B, C]

予想される LIFO 順序でそれらをストリームに出力しないのはなぜですか? これは、スタックからすべてのアイテムを正しい (LIFO) 順序でリストにフラッシュする正しい方法ですか?

4

1 に答える 1

50

コメントで既に述べたように、私たちはよくテストされたDequeインターフェイスを推奨しています。

Stackしかし、使用してはいけない理由を教えてください。

まず、Java Doc. のスタックは、次のように述べています。

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

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

JavaDocを参照してください。

それで、それはStackクラスの問題です。

Martin Fowler が著書 Refactoring: Improving the Design of Existing Code でリファクタリング メソッドReplace Inheritance with Delegationで既に述べたように、Stack は Vector から継承すべきではありません。

不適切な継承の古典的な例の 1 つは、スタックを vector のサブクラスにすることです。Java 1.1 はそのユーティリティでこれを行います (いたずらっ子!) [6, p. 288]

代わりに、下の図のように委任を使用する必要がありました。これも本からのものです。

ここも参照してください:継承を委任に置き換える

継承を委任に置き換える

しかし、なぜこれが問題なのですか:

スタックには 5 つのメソッドしかないため:

  1. ポップ
  2. 押す
  3. が空です
  4. 探す
  5. サイズ

size()およびisEmpty()Vectorクラスから継承され、 の他のメソッドVectorは使用されません。しかし、継承によって、他のメソッドがStack意味をなさないクラスに転送されます。

そして、ファウラーはこの問題について次のように述べています。

サブクラスではあるが、スーパークラス関数の一部のみを使用しているという慣例を使用して、状況に対処することができます。しかし、その結果、意図が別のものである場合に 1 つのことを言うコードが作成されます。混乱を取り除く必要があります。

これは、インターフェース分離の原則を傷つけます

それは言う:

クライアントは、使用しないインターフェースに依存することを強いられるべきではありません。


VectorおよびStackクラスのソース コードを確認すると、Stack クラスがクラスからspliteratorメソッドとVectorSpliteratorinnerClass を継承していることがわかりますVector

このメソッドはCollection、impl へのインターフェースによって使用されます。ストリーム メソッドのデフォルト バージョン:

default Stream<E> stream() {
  return StreamSupport.stream(spliterator(), false);
}

Vectorそのため、 andStackクラスの単純な使用は避けてください。

[6] Refactoring: Improving the Design of Existing Code Fowler, Martin year 1997

于 2015-05-22T06:36:31.827 に答える