使用Java 8流API查找enum值

时间:2023-01-26 15:07:12

Suppose there is a simple enum called Type defined like this:

假设有一个简单的enum,称为类型定义如下:

enum Type{
    X("S1"),
    Y("S2");

    private String s;

    private Type(String s) {
        this.s = s;
    }
}

Finding the correct enum for given s is trivially done with static method with for-loop (assume the method is defined inside enum), e.g.:

为给定的s找到正确的enum通常使用带有for循环的静态方法(假设方法是在enum中定义的),例如:

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));
}

I think the functional equivalent of this expressed with Stream API would be something like this:

我认为与流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));});
}

How could we write this better and simpler? This code feels coerced and not very clear. The reduce() especially seems clunky and abused as it doesn't accumulate anything, performs no calculation and always simply returns t1 (provided the filter returns one value - if it doesn't that's clearly a disaster), not to mention t2 is there superfluous and confusing. Yet I couldn't find anything in Stream API that simply somehow returns directly a T from a Stream<T>.

怎么写得更好更简单呢?这个代码感觉是*的,不太清楚。reduce()看起来特别笨拙和滥用,因为它不积累任何东西,不执行任何计算,并且总是只返回t1(如果过滤器返回一个值——如果没有的话,那显然是一个灾难),更不用说t2是否存在多余和混乱。但是我在流API中找不到任何直接从流中返回T的东西。

Is there a better way?

有更好的方法吗?

6 个解决方案

#1


52  

I would use findFirst instead:

我将使用findFirst代替:

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


Though a Map could be better in this case:

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;
    }
}

I learnt this trick from this answer. Basically the class loader initializes the static classes before the enum class, which allows you to fill the Map in the enum constructor itself. Very handy !

我从这个答案中学到了这个诀窍。基本上,类装入器在enum类之前初始化静态类,该类允许您在enum构造函数本身中填充映射。非常方便!

Hope it helps ! :)

希望它可以帮助!:)

#2


12  

The accepted answer works well, but if you want to avoid creating a new stream with a temporary array you could use EnumSet.allOf().

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

EnumSet.allOf(Type.class)
       .stream()
       .filter(e -> e.s.equals(val))
       .findFirst()
       .orElseThrow(String.format("Unsupported type %s.", val));

#3


4  

How about using findAny() instead of reduce?

用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)));
}

#4


3  

Arrays.stream(Type.values()).filter(v -> v.s.equals(val)).findAny().orElseThrow(...);

#5


0  

I can't add a comment yet, so I am posting an answer to complement the above answer, just following the same idea but using java 8 approach:

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

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

#6


0  

You need a getter for String s. In the example below this method is getDesc():

您需要一个用于字符串s的getter。在下面的示例中,这个方法是getDesc():

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

#1


52  

I would use findFirst instead:

我将使用findFirst代替:

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


Though a Map could be better in this case:

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;
    }
}

I learnt this trick from this answer. Basically the class loader initializes the static classes before the enum class, which allows you to fill the Map in the enum constructor itself. Very handy !

我从这个答案中学到了这个诀窍。基本上,类装入器在enum类之前初始化静态类,该类允许您在enum构造函数本身中填充映射。非常方便!

Hope it helps ! :)

希望它可以帮助!:)

#2


12  

The accepted answer works well, but if you want to avoid creating a new stream with a temporary array you could use EnumSet.allOf().

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

EnumSet.allOf(Type.class)
       .stream()
       .filter(e -> e.s.equals(val))
       .findFirst()
       .orElseThrow(String.format("Unsupported type %s.", val));

#3


4  

How about using findAny() instead of reduce?

用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)));
}

#4


3  

Arrays.stream(Type.values()).filter(v -> v.s.equals(val)).findAny().orElseThrow(...);

#5


0  

I can't add a comment yet, so I am posting an answer to complement the above answer, just following the same idea but using java 8 approach:

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

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

#6


0  

You need a getter for String s. In the example below this method is getDesc():

您需要一个用于字符串s的getter。在下面的示例中,这个方法是getDesc():

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