Finding enum value with Java 8 Stream API
假设有一个名为type的简单枚举定义如下:
对于给定的
1 2 3 4 5 6 7 | 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)); } |
我认为用流API表示的这个函数等价物应该是这样的:
1 2 3 4 5 6 | 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));}); } |
我们怎么能写得更好更简单呢?这段代码让人觉得是被胁迫的,不太清楚。
有更好的方法吗?
我用
1 2 3 4 | return Arrays.stream(Type.values()) .filter(e -> e.s.equals(val)) .findFirst() .orElseThrow(() -> new IllegalStateException(String.format("Unsupported type %s.", val))); |
虽然在这种情况下,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | enum Type{ X("S1"), Y("S2"); private static class Holder { static Map<String, Type> 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; } } |
我从这个答案中学到了这个技巧。基本上,类加载器在枚举类之前初始化静态类,这允许您在枚举构造函数本身中填充
希望有帮助!:)
接受的答案很好,但是如果您想避免使用临时数组创建新的流,可以使用
1 2 3 4 5 | EnumSet.allOf(Type.class) .stream() .filter(e -> e.s.equals(val)) .findFirst() .orElseThrow(String.format("Unsupported type %s.", val)); |
用
1 2 3 4 5 6 | 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))); } |
1 |
我认为亚历克西斯C的第二个答案(亚历克西斯C的答案)在复杂性方面是好的。而不是每次使用
1 2 3 4 | 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)访问类型的代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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流解决问题的问题。相反,这个答案扩展了公认答案中提出的基于
所以这里是:我提议引入一个特殊的助手类,我将它命名为
假设
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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)真正可读:
1 2 3 4 5 6 7 | 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 } |
最后,这里是
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | 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 } |
注意:
我在这里用了番石榴的
如果您不介意上面的方法中没有延迟初始化,那么您可以延迟生成
你需要一个字符串的getter,但这是我使用的模式:
1 2 3 4 5 6 7 8 9 | 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); } |
不适用于循环,只适用于流。快速查找,而不是每次调用方法时都生成流。
你需要一个字符串的getter。在下面的示例中,此方法是
1 2 3 |
我不能添加评论,所以我发布一个答案来补充上述答案,只是遵循相同的想法,但使用Java 8的方法:
1 2 3 4 5 | public static Type find(String val) { return Optional .ofNullable(Holder.MAP.get(val)) .orElseThrow(() -> new IllegalStateException(String.format("Unsupported type %s.", val))); } |