使用Java8流API查找枚举值
假设有一个名为Type的简单枚举定义如下:使用Java8流API查找枚举值,java,enums,functional-programming,java-8,java-stream,Java,Enums,Functional Programming,Java 8,Java Stream,假设有一个名为Type的简单枚举定义如下: enum Type{ X("S1"), Y("S2"); private String s; private Type(String s) { this.s = s; } } private static Type find(String val) { return Arrays.stream(Type.values()) .filter(e -> e
enum Type{
X("S1"),
Y("S2");
private String s;
private Type(String s) {
this.s = s;
}
}
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));});
}
对于给定的s
,使用带有for循环的静态方法(假设该方法是在enum中定义的)查找正确的enum非常简单,例如:
我认为用流API表示的功能等价物如下:
enum Type{
X("S1"),
Y("S2");
private String s;
private Type(String s) {
this.s = s;
}
}
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
(如果过滤器返回一个值-如果它不返回,那显然是一场灾难),更不用说t2
是否有多余和混乱。然而,我在流API中找不到任何简单地直接从流返回t
的东西
有更好的方法吗?我会使用findFirst
:
return Arrays.stream(Type.values())
.filter(e -> e.s.equals(val))
.findFirst()
.orElseThrow(() -> new IllegalStateException(String.format("Unsupported type %s.", val)));
虽然在这种情况下,Map
可能更好:
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;
}
}
枚举类型{
X(“S1”),
Y(“S2”);
私有静态类持有者{
静态映射映射=新的HashMap();
}
私有类型(字符串s){
持有者。地图。放置(s,本);
}
公共静态类型查找(字符串val){
类型t=Holder.MAP.get(val);
如果(t==null){
抛出新的IllegalStateException(String.format(“不支持的类型%s.”,val));
}
返回t;
}
}
我从中学会了这个把戏。基本上,类加载器在枚举类之前初始化静态类,这允许您在枚举构造函数本身中填充Map
。非常方便
希望有帮助!:) 用findAny()
代替reduce
怎么样
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)));
}
我还不能添加评论,所以我发布了一个答案来补充上述内容,只是遵循相同的想法,但使用java 8方法:
public static Type find(String val) {
return Optional
.ofNullable(Holder.MAP.get(val))
.orElseThrow(() -> new IllegalStateException(String.format("Unsupported type %s.", val)));
}
接受的答案很有效,但是如果您想避免使用临时数组创建新流,可以使用EnumSet.allOf()
你需要一个字符串s的getter。
在下面的示例中,此方法是getDesc()
:
我知道这个问题很老,但我是从一个重复的问题来的。我的答案并不是严格地回答OP关于如何使用Java流解决问题的问题。相反,此答案扩展了中提出的基于映射的解决方案,使其更易于管理(IMHO)
这里就是:我建议引入一个特殊的助手类,我将其命名为EnumLookup
假设类型
枚举编写得稍微好一点(有意义的字段名+getter),我将向它注入一个枚举查找
常量,如下所示:
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;
}
}
请注意:
我在这里用了番石榴,但是可以用常规的HashMap
或LinkedHashMap
来代替
如果您不介意在上述方法中缺少延迟初始化,您可以延迟EnumLookup
的构建,直到第一次调用byCode
方法(例如,使用,如中的)
我认为Alexis C.()的第二个答案在复杂性方面是好的。而不是每次使用
return Arrays.stream(Type.values())
.filter(e -> e.s.equals(val))
.findFirst()
.orElseThrow(() -> new IllegalStateException(String.format("Unsupported type %s.", val)));
通过将所有元素放入映射,可以在加载类时使用O(n)时间,然后使用映射以恒定时间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);
}
}
枚举类型{
X(“S1”),
Y(“S2”);
私有最终字符串代码;
私有静态映射=新HashMap();
静止的{
Arrays.stream(Type.values()).forEach(Type->mapping.put(Type.getCode(),Type));
}
类型(字符串代码){
this.code=代码;
}
公共字符串getCode(){
返回码;
}
公共静态类型forCode(最终字符串代码){
返回mapping.get(代码);
}
}
字符串s需要一个getter,但我使用的模式是:
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);
}
private静态最终映射类型\u映射=
集合。不可修改的映射(
EnumSet.allOf(Type.class)
.stream()
.collect(Collectors.toMap(Type::get,e->e));
公共静态类型查找(字符串s){
返回类型\u MAP.get(s);
}
没有循环,只有流。快速查找,而不是每次调用该方法时都构建流。我知道这个评论不会被任何人投票,但与Java 8一样好的是,您不必对每个问题都使用stream
s。你的for-loop方法比任何使用Stream
s的方法都更清晰(更快)。@pbabcdefp好吧,我认为这是一个好的评论,但如果我投了更高的票,那么你评论中的第一句话就错了,这意味着我必须再次投反对票,然后我会再次认为这是一个好的评论,所以我必须投更高的票,但是第一句话就错了。。。我想我即将抛出StackOverflowException
…@pbabcdefp-这可能是一个意见问题,但我发现lambdas越来越优于迭代,而且清晰性几乎总是胜过效率。我确信我尝试了findFirst()
,在IDEA中遇到了一些奇怪的编译错误,并编写了reduce()
变体。无论如何,我对你所有的答案都投了赞成票,但我觉得第一个
比任何一个
都要清楚,所以我选择了它。谢谢你的帮助!这是一个非常巧妙的技巧,我特别喜欢JVM如何保证串行映射填充-非常好。只有一个小建议-我们可以通过去掉字段s
使代码更加紧凑,因为它在其他地方没有使用。findAny()
而不是findFirst()
?--如果保证有一个(或零个)匹配项,那么潜在的findAny()
将更快地得到答案(尽管我想枚举是否足够大以便搜索可以并行化值得怀疑)@slim随着流的顺序,findFirst()
在多次匹配的情况下,表现出与原始java 8之前的代码相同的行为。虽然我
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
}
return Arrays.stream(Type.values())
.filter(e -> e.s.equals(val))
.findFirst()
.orElseThrow(() -> new IllegalStateException(String.format("Unsupported type %s.", val)));
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);
}
}
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);
}