使用Java 8 Stream API查找枚举值

qua*_*tum 37 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

s使用for-loop的静态方法(假设该方法在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));
}
Run Code Online (Sandbox Code Playgroud)

我认为使用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));});
}
Run Code Online (Sandbox Code Playgroud)

我们怎么能写得更好更简单?这段代码感觉很强烈,不太清楚.该reduce()特别是似乎笨重和滥用,因为它没有任何积累,不进行计算,并始终直接返回t1(所提供的过滤器返回一个值-如果没有这显然是一个灾难),更何况t2是有多余的混乱.然而,我在Stream API中找不到任何东西,只是以某种方式T从a 直接返回a Stream<T>.

有没有更好的办法?

Ale*_* C. 78

我会findFirst改用:

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


虽然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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

希望能帮助到你 !:)

  • 这是一个非常好的技巧,我特别喜欢JVM如何保证串行地图填充 - 非常好.只是一个小建议 - 我们可以通过删除字段`s来使代码更紧凑,因为它在其他地方没用. (4认同)

小智 15

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

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

  • 查看JDK源代码,`Arrays.stream(Type.values())`在内部克隆一个数组,然后创建一个新的流 - 而`EnumSet.allOf(Type.class).stream()`在内部创建一个新的EnumSet,将所有枚举值添加到其中,然后创建新流.这个解决方案对我来说看起来更好,但是使用它的决定不应该只基于关于创建了多少对象的假设. (7认同)

spr*_*ter 5

Arrays.stream(Type.values()).filter(v -> v.s.equals(val)).findAny().orElseThrow(...);
Run Code Online (Sandbox Code Playgroud)