使用Java 8 Stream API查找枚举值

假设有一个名为Type的简单枚举,如下所示:

enum Type{ X("S1"), Y("S2"); private String s; private Type(String s) { this.s = s; } } 

使用for-loop的静态方法(假设该方法在enum中定义)可以轻松地找到给定s的正确枚举,例如:

 private static Type find(String val) { for (Type e : Type.values()) { if (esequals(val)) return e; } throw new IllegalStateException(String.format("Unsupported type %s.", val)); } 

我认为用Stream API表示的function等价物将是这样的:

 private static Type find(String val) { return Arrays.stream(Type.values()) .filter(e -> esequals(val)) .reduce((t1, t2) -> t1) .orElseThrow(() -> {throw new IllegalStateException(String.format("Unsupported type %s.", val));}); } 

我们怎么能写得更好更简单呢? 这段代码感觉很强烈,不太清楚。 reduce()特别显得笨拙和滥用,因为它不会累积任何东西,不执行任何计算并且总是简单地返回t1 (假设filter返回一个值 – 如果它不是那显然是灾难),更不用说t2在那里多余和混乱。 然而,我在Stream API中找不到任何东西,只是以某种方式从Stream直接返回Stream

有没有更好的办法?

我会改用findFirst

 return Arrays.stream(Type.values()) .filter(e -> esequals(val)) .findFirst() .orElseThrow(() -> new IllegalStateException(String.format("Unsupported type %s.", val))); 

虽然在这种情况下Map可能会更好:

 enum Type{ X("S1"), Y("S2"); private static class Holder { static Map MAP = new HashMap<>(); } private Type(String s) { Holder.MAP.put(s, this); } public static Type find(String val) { Type t = Holder.MAP.get(val); if(t == null) { throw new IllegalStateException(String.format("Unsupported type %s.", val)); } return t; } } 

我从这个答案中学到了这个技巧。 基本上,类加载器在枚举类之前初始化静态类,这允许您在枚举构造函数本身中填充Map 。 非常便利 !

希望能帮助到你 ! 🙂

接受的答案效果很好,但如果您想避免使用临时数组创建新流,则可以使用EnumSet.allOf()

 EnumSet.allOf(Type.class) .stream() .filter(e -> esequals(val)) .findFirst() .orElseThrow(String.format("Unsupported type %s.", val)); 
 Arrays.stream(Type.values()).filter(v -> vsequals(val)).findAny().orElseThrow(...); 

如何使用findAny()而不是reduce

 private static Type find(String val) { return Arrays.stream(Type.values()) .filter(e -> esequals(val)) .findAny() .orElseThrow(() -> new IllegalStateException(String.format("Unsupported type %s.", val))); } 

我知道这个问题已经过时但是我从一个副本来到这里。 我的回答并不是严格回答OP关于如何使用Java Streams解决问题的问题。 相反,这个答案扩展了在接受的答案中提出的基于Map的解决方案,使其变得更加(IMHO)可管理。

所以这就是:我建议引入一个名为EnumLookup的特殊助手类。

假设Type枚举稍微好一些(有意义的字段名+ getter),我会向它注入一个EnumLookup常量,如下所示:

 enum Type { X("S1"), Y("S2"); private static final EnumLookup 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 byCode() { return BY_CODE; } } 

然后用法(再次,IMO)真正可读:

 Type type = Type.byCode().get("S1"); // returns Type.X Optional optionalType = Type.byCode().find("S2"); // returns Optional(Type.Y) if (Type.byCode().contains("S3")) { // returns false // logic } 

最后,这是EnumLookup助手类的代码:

 public final class EnumLookup, ID> { private final Class enumClass; private final ImmutableMap valueByIdMap; private final String idTypeName; private EnumLookup(Class enumClass, ImmutableMap 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 find(ID id) { return Optional.ofNullable(valueByIdMap.get(id)); } //region CONSTRUCTION public static , ID> EnumLookup of( Class enumClass, Function idExtractor, String idTypeName) { ImmutableMap valueByIdMap = Arrays.stream(enumClass.getEnumConstants()) .collect(ImmutableMap.toImmutableMap(idExtractor, Function.identity())); return new EnumLookup<>(enumClass, valueByIdMap, idTypeName); } public static > EnumLookup byName(Class enumClass) { return of(enumClass, Enum::name, "enum name"); } //endregion } 

注意:

  1. 我在这里使用了Guava的ImmutableMap ,但是可以使用常规的HashMapLinkedHashMap

  2. 如果你介意在上面的方法中缺少延迟初始化,你可以延迟构建EnumLookup直到首次调用byCode方法(例如使用lazy-holder惯用法 ,就像在接受的答案中一样 )

我还不能添加评论,所以我发布了一个答案来补充上面的答案 ,只是遵循相同的想法,但使用java 8方法:

 public static Type find(String val) { return Optional .ofNullable(Holder.MAP.get(val)) .orElseThrow(() -> new IllegalStateException(String.format("Unsupported type %s.", val))); } 

你需要一个String的getter。 在下面的示例中,此方法是getDesc()

 public static StatusManifestoType getFromValue(String value) { return Arrays.asList(values()).stream().filter(t -> t.getDesc().equals(value)).findAny().orElse(null); }