次のように定義されたTypeという単純な列挙型があると仮定します。
_enum Type{
X("S1"),
Y("S2");
private String s;
private Type(String s) {
this.s = s;
}
}
_
指定されたs
の正しい列挙型を見つけることは、forループを使用した静的メソッドで簡単に行われます(メソッドが列挙型内で定義されていると仮定します)。例:
_private static Type find(String val) {
for (Type e : Type.values()) {
if (e.s.equals(val))
return e;
}
throw new IllegalStateException(String.format("Unsupported type %s.", val));
}
_
Stream APIで表現されたこれと機能的に同等なものは、次のようなものになると思います。
_private static Type find(String val) {
return Arrays.stream(Type.values())
.filter(e -> e.s.equals(val))
.reduce((t1, t2) -> t1)
.orElseThrow(() -> {throw new IllegalStateException(String.format("Unsupported type %s.", val));});
}
_
どうすればこれをより簡単に書くことができますか?このコードは強制されており、あまり明確ではありません。 reduce()
は、特に何も蓄積せず、計算を実行せず、常に単に_t1
_を返すため、不格好で乱用されているようです(フィルターが1つの値を返す場合-それが明らかに災害ではない場合) )、_t2
_は言うまでもなく、余計で紛らわしいものがあります。しかし、Stream APIには、単に_Stream<T>
_からT
を直接返すようなものは何も見つかりませんでした。
もっと良い方法はありますか?
受け入れられた答えはうまく機能しますが、一時配列で新しいストリームを作成したくない場合は、EnumSet.allOf()
を使用できます。
EnumSet.allOf(Type.class)
.stream()
.filter(e -> e.s.equals(val))
.findFirst()
.orElseThrow(String.format("Unsupported type %s.", val));
Arrays.stream(Type.values()).filter(v -> v.s.equals(val)).findAny().orElseThrow(...);
reduce
の代わりにfindAny()
を使用してはどうですか?
private static Type find(String val) {
return Arrays.stream(Type.values())
.filter(e -> e.s.equals(val))
.findAny()
.orElseThrow(() -> new IllegalStateException(String.format("Unsupported type %s.", val)));
}
Alexis Cの2番目の答え( Alexis C.の答え )は、複雑さに関しては良い答えだと思います。 O(n)で検索する代わりに、コードを探すたびに
return Arrays.stream(Type.values())
.filter(e -> e.s.equals(val))
.findFirst()
.orElseThrow(() -> new IllegalStateException(String.format("Unsupported type %s.", val)));
すべての要素をマップに配置することにより、クラスのロード時にO(n) timeを使用し、一定時間で型のコードにアクセスすることができますO(1)マップを使用します。
enum Type{
X("S1"),
Y("S2");
private final String code;
private static Map<String, Type> mapping = new HashMap<>();
static {
Arrays.stream(Type.values()).forEach(type-> mapping.put(type.getCode(), type));
}
Type(String code) {
this.code = code;
}
public String getCode() {
return code;
}
public static Type forCode(final String code) {
return mapping.get(code);
}
}
私はこの質問が古いことを知っていますが、私はここから重複して来ました。私の答えは、問題を解決する方法についてのOPの質問に厳密に答えているわけではありませんJava Streamsを使用して。代わりに、この答え 受け入れられた答え で提案されているMap
ベースのソリューションを拡張して、より(IMHO)管理しやすくします。
EnumLookup
という名前の特別なヘルパークラスを導入することを提案します。
Type
列挙がわずかに記述されていると仮定すると(意味のあるフィールド名+ゲッター)、以下のようにEnumLookup
定数を注入します。
enum Type {
X("S1"),
Y("S2");
private static final EnumLookup<Type, String> BY_CODE = EnumLookup.of(Type.class, Type::getCode, "code");
private final String code;
Type(String code) {
this.code = code;
}
public String getCode() {
return code;
}
public static EnumLookup<Type, String> byCode() {
return BY_CODE;
}
}
使用法は(再び、IMO)本当に読みやすくなります:
Type type = Type.byCode().get("S1"); // returns Type.X
Optional<Type> optionalType = Type.byCode().find("S2"); // returns Optional(Type.Y)
if (Type.byCode().contains("S3")) { // returns false
// logic
}
最後に、EnumLookup
ヘルパークラスのコードを示します。
public final class EnumLookup<E extends Enum<E>, ID> {
private final Class<E> enumClass;
private final ImmutableMap<ID, E> valueByIdMap;
private final String idTypeName;
private EnumLookup(Class<E> enumClass, ImmutableMap<ID, E> valueByIdMap, String idTypeName) {
this.enumClass = enumClass;
this.valueByIdMap = valueByIdMap;
this.idTypeName = idTypeName;
}
public boolean contains(ID id) {
return valueByIdMap.containsKey(id);
}
public E get(ID id) {
E value = valueByIdMap.get(id);
if (value == null) {
throw new IllegalArgumentException(String.format(
"No such %s with %s: %s", enumClass.getSimpleName(), idTypeName, id
));
}
return value;
}
public Optional<E> find(ID id) {
return Optional.ofNullable(valueByIdMap.get(id));
}
//region CONSTRUCTION
public static <E extends Enum<E>, ID> EnumLookup<E, ID> of(
Class<E> enumClass, Function<E, ID> idExtractor, String idTypeName) {
ImmutableMap<ID, E> valueByIdMap = Arrays.stream(enumClass.getEnumConstants())
.collect(ImmutableMap.toImmutableMap(idExtractor, Function.identity()));
return new EnumLookup<>(enumClass, valueByIdMap, idTypeName);
}
public static <E extends Enum<E>> EnumLookup<E, String> byName(Class<E> enumClass) {
return of(enumClass, Enum::name, "enum name");
}
//endregion
}
ご了承ください:
ここではGuavaの ImmutableMap
を使用しましたが、代わりに通常のHashMap
またはLinkedHashMap
を使用できます。
上記のアプローチで遅延初期化の欠如を気にする場合は、EnumLookup
メソッドが最初に呼び出されるまでbyCode
の構築を遅らせることができます(たとえば、 lazy-holder idiom =、 受け入れられた回答 )のように
Stringにはゲッターが必要ですが、これは私が使用するパターンです:
private static final Map<String, Type> TYPE_MAP =
Collections.unmodifiableMap(
EnumSet.allOf(Type.class)
.stream()
.collect(Collectors.toMap(Type::getS, e -> e)));
public static Type find(String s) {
return TYPE_MAP.get(s);
}
Forループではなく、ストリームのみ。メソッドが呼び出されるたびにストリームを構築するのではなく、クイックルックアップ。
Stringのゲッターが必要です。以下の例では、このメソッドはgetDesc()
です:
public static StatusManifestoType getFromValue(String value) {
return Arrays.asList(values()).stream().filter(t -> t.getDesc().equals(value)).findAny().orElse(null);
}
私はまだコメントを追加することができないので、上記の answer を補完する回答を投稿します。同じアイデアに従いますが、Java 8アプローチを使用します。
public static Type find(String val) {
return Optional
.ofNullable(Holder.MAP.get(val))
.orElseThrow(() -> new IllegalStateException(String.format("Unsupported type %s.", val)));
}