web-dev-qa-db-ja.com

Java 8列挙型を操作する方法

Java 8で列挙型のすべての値を操作するのに最適な方法は何ですか。具体的には、すべての値を取得してどこかに追加する必要がある場合、たとえば次の列挙型があること:

public enum Letter {
 A, B, C, D;
}

もちろん次のことができます。

for (Letter l : Letter.values()) {
    foo(l);
}

ただし、次のメソッドを列挙定義に追加することもできます。

public static Stream<Letter> stream() {
    return Arrays.stream(Letter.values());
}

次に、上記のforを次のように置き換えます。

Letter.stream().forEach(l -> foo(l));

このアプローチは大丈夫ですか、それとも設計やパフォーマンスに何らかの欠陥がありますか?さらに、列挙型にstream()メソッドがないのはなぜですか?

57
Rumal

3つの質問:3つの部分からなる回答:

デザインの観点からは大丈夫ですか?

絶対に。何も問題はありません。列挙に対して多くの反復処理を行う必要がある場合、ストリームAPIがクリーンな方法であり、小さなメソッドの背後にボイラープレートを隠すのは問題ありません。 OldCumudgeon ’s version の方が良いと思いますが.

パフォーマンスの観点からは大丈夫ですか?

ほとんど問題ではありません。ほとんどの場合、列挙型はそれほど大きくありません。したがって、いずれかの方法でオーバーヘッドが発生しても、99.9%のケースではおそらく問題になりません。

もちろん、0.1%があります。その場合:実際のデータと消費者で適切に測定します。

賭けなければならなかったなら、for eachループはより高速になります。メモリモデルにより直接的にマッピングされますが、パフォーマンスについて話すときは推測せず、実際にチューニングが必要になる前にチューニングしないでください。最初に正しい方法でコードを書き、2番目に読みやすくしてから、コードスタイルのパフォーマンスを心配します。

EnumがStream APIに適切に統合されないのはなぜですか?

JavaのStream APIを他の多くの言語の同等のAPIと比較すると、深刻な制限があります。欠落しているさまざまな要素があります(たとえば、再利用可能なストリームとストリームとしてのオプション)。一方、Stream APIの実装は、APIにとって大きな変化でした。理由により複数回延期されました。だから、オラクルは、変更を最も重要なユースケースに制限したかったと思います。とにかく列挙型はそれほど使用されません。もちろん、すべてのプロジェクトにはそれらがいくつかありますが、リストやその他のコレクションの数と比較しても何もありません。 Enumを使用している場合でも、多くの場合、それを繰り返し処理することはありません。一方、リストとセットは、ほぼ毎回繰り返される可能性があります。これらが列挙型がストリームの世界に独自のアダプターを取得しなかった理由だと思います。今後のバージョンでこれがさらに追加されるかどうかを確認します。そして、それまではいつでもArrays.stream

26
Jens Schauder

EnumSet に行きます。 forEach()Iterableで定義されているため、ストリームの作成を完全に回避できます。

EnumSet.allOf(Letter.class).forEach(x -> foo(x));

または、メソッド参照を使用して:

EnumSet.allOf(Letter.class).forEach(this::foo);

それでも、オールドスクールのforループは少しシンプルに感じています。

for (Letter x : Letter.values()) {
    foo(x);
}
27
Natix

私のguessは、列挙型のサイズが制限されている(つまり、サイズは言語によって制限されていないが使用法によって制限されている)ため、nativeストリームAPI。変換を操作し、ストリーム内の要素を再収集する必要がある場合、ストリームは非常に優れています。これらは、Enumの一般的な使用例ではありません(通常、enum値を反復処理しますが、それらを変換、マッピング、収集する必要はほとんどありません)。

各要素に対してアクションを実行するだけでよい場合は、おそらくforEachメソッドのみを公開する必要があります。

     public static void forEach(Consumer<Letter> action) {
            Arrays.stream(Letter.values()).forEach(action);
     }

     .... //example of usage
     Letter.forEach(e->System.out.println(e));
12
Giovanni

Enum定数のStreamを取得する最短のコードはStream.of(Letter.values())であると思います。 Letter.values().stream()ほど良いものではありませんが、それは特に列挙型ではなく配列の問題です。

さらに、列挙型にstream()メソッドがないのはなぜですか?

あなたは正しい呼び出しがLetter.stream()であることは正しいです。残念ながら、クラスは同じシグネチャを持つ2つのメソッドを持つことができないため、すべての列挙に静的メソッドstream()を暗黙的に追加することはできません(すべての列挙に暗黙的に追加される静的メソッドvalues())これは、stream()と呼ばれるパラメーターのない静的またはインスタンスメソッドを既に持っている既存の列挙型をすべて破壊するためです。

このアプローチは大丈夫ですか?

私はそう思う。欠点は、streamが静的メソッドであるため、コードの重複を回避する方法がないことです。すべての列挙に個別に追加する必要があります。

5
Paul Boddington