当前位置: 首页 > 面试题库 >

使用Java 8 Stream API查找枚举值

段干安和
2023-03-14
问题内容

假设有一个名为Type的简单枚举定义如下:

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

    private String s;

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

找到具有给定s值的正确枚举通常是通过带for循环的静态方法(假设该方法在枚举内部定义)完成的,例如:

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

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

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

有没有更好的办法?


问题答案:

我会findFirst改用:

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

尽管Map在这种情况下a 可能更好:

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

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

您想要一个Map<String, Code>,但是如何整齐地填充它呢?枚举不允许您在初始化枚举实例之前初始化静态字段,但是有一个巧妙的小技巧,称为“ 按需初始化holder idiom”,它使使用此功能所需的静态初始化映射变得容易实现:

public enum Code {
    CODE_1("string1"),
    CODE_2("string2"),
    CODE_3("string3"),
    // etc
    ;

    private static class Holder {
        static Map<String, Code> CODE_MAP = new HashMap<>();
    }

    private final String code;

    private Code(String code) {
        this.code = code;
        Holder.CODE_MAP.put(code, this);
    }

    public String getCode() {
        return this.code;
    }

    public Code convertFromString(String code) {
        return CODE_MAP.get(code);
    }
}

之所以可行,是因为类加载器在初始化枚举类之前先初始化内部静态类,因此将映射分配为在枚举实例初始化期间准备加载。

没有循环。无需特殊代码即可加载地图(在构造函数中完成)。最少的代码。

希望能帮助到你 !:)



 类似资料:
  • 假设有一个名为 Type 的简单枚举,定义如下: 为给定的<code>s</code>找到正确的枚举是用带有for循环的静态方法完成的(假设该方法在枚举中定义),例如: 我认为用Stream API表达的功能等价物是这样的: 我们怎样才能写得更好,更简单?这段代码感觉是被迫的,不是很清楚。尤其显得笨拙和滥用,因为它不积累任何东西,不执行任何计算,总是简单地返回(前提是过滤器返回一个值 - 如果它没

  • 假设我有一个枚举: 如果我有一个数字,我可以这样得到枚举键: 然而,这给了我一个字符串。我怎样才能得到一个Color类型的变量呢?即:

  • 枚举定义- 存储库- 服务电话- 例外情况- 为什么它会抛出此异常?我已经定义了@Enumerated(EnumType.STRING),所以它应该期望存储在db中的字符串类型。

  • 我试图找到在Kotlin中对枚举进行“反向查找”的最佳方法。我从Effective Java中得到的一个收获是,在枚举中引入了一个静态映射来处理反向查找。通过一个简单的枚举将其移植到Kotlin上,我会得到如下所示的代码: 我的问题是,这是最好的方法,还是有更好的方法?如果我有几个遵循类似模式的枚举怎么办?在Kotlin中是否有一种方法可以使此代码在枚举之间更加可重用?

  • 使用 use 声明,这样就不必手动加上作用域了: // 隐藏未使用代码警告的属性。 #![allow(dead_code)] enum Status { Rich, Poor, } enum Work { Civilian, Soldier, } fn main() { // 明确地 `use` 各个名称使他们直接可用而不需要手动加上作用域。 use

  • 当我们需要定义常量时,一个办法是用大写变量通过整数来定义,例如月份: JAN = 1 FEB = 2 MAR = 3 ... NOV = 11 DEC = 12 好处是简单,缺点是类型是int,并且仍然是变量。 更好的方法是为这样的枚举类型定义一个class类型,然后,每个常量都是class的一个唯一实例。Python提供了Enum类来实现这个功能: from enum import Enum