我有下面的枚举。我需要通过代码获取描述。它正在工作,但仍可以改进吗?

public enum Maps {

    COLOR_RED("ABC", "abc description");

    private final String code;
    private final String description;
    private static Map<String, String> mMap;

    private Maps(String code, String description) {
        this.code = code;
        this.description = description;
    }
    public String getCode() {
        return code;
    }

    public String getDescription() {
        return description;
    }


    public static String getDescriptionByCode(String code) {
        if (mMap == null) {
            initializeMapping();
        }
        if (mMap.containsKey(code)) {
            return mMap.get(code);
        }
        return null;
    }

    private static void initializeMapping() {
        mMap = new HashMap<String, String>();
        for (Maps s : Maps.values()) {
            mMap.put(s.code, s.description);
        }
    }
}


#1 楼

您的代码非常整洁,只有一件事我需要更改:

private static Map<String, String> mMap;




private static final Map<String, String> mMap = Collections.unmodifiableMap(initializeMapping());


原因:


通过不声明final并且不使用对unmodifiableMap的调用是可变的,因此可以使用反射修改引用或在.remove("ABC")上使用地图。将其声明为final可以确保引用的映射不能更改,而unmodifiableMap可以确保对映射本身无法进行任何更改。
多线程问题。按照目前的情况,如果两个线程同时调用getDescriptionByCode方法,则将初始化两次映射,这是不需要的。

当然,这也需要对initializeMapping()稍作更改:

private static Map<String, String> initializeMapping() {
    Map<String, String> mMap = new HashMap<String, String>();
    for (Maps s : Maps.values()) {
        mMap.put(s.code, s.description);
    }
    return mMap;
}


除此之外,一切看起来都不错。干得好!

评论


\ $ \ begingroup \ $
要解决这些多线程问题,请使用静态初始化块,该块必须运行一次。
\ $ \ endgroup \ $
– wchargin
14年4月4日在16:33

\ $ \ begingroup \ $
@WChargin AFAIK私有静态最终Map mMap = Collections.unmodifiableMap(initializeMapping());还保证只能运行一次。初始化是作为静态最终变量完成的。
\ $ \ endgroup \ $
–西蒙·福斯伯格
14年4月4日在16:38

\ $ \ begingroup \ $
WTF? mMap是私有的;没有什么可以更改它引用的对象或从地图添加/删除条目的。是的,您可以使用反射,但是在几乎所有情况下,尝试设计类以防止反射将其内部结构弄乱都是一个糟糕的主意。
\ $ \ endgroup \ $
–布赖恩·戈登(Brian Gordon)
15年2月18日在22:22

\ $ \ begingroup \ $
@BrianGordon使其私有且不可修改,可以防止您将来意外编写修改该代码的代码。另外,在检查代码时,可以很容易地看到代码的预期用途。我在这里不是在谈论反思。
\ $ \ endgroup \ $
–西蒙·福斯伯格
2015年2月18日在23:26



#2 楼

您可以将以下内容更改为:


if (mMap.containsKey(code)) {
    return mMap.get(code);
}
return null;



到:

return mMap.get(code);


HashMap.get()如果地图中没有这样的键,则返回null

评论


\ $ \ begingroup \ $
实际上,您应该这样做。它更短并且更好地利用了api。您可以添加注释(对于不熟悉Map api的ppl),以告知是否不包含键,则返回null
\ $ \ endgroup \ $
–RobAu
2014年4月4日15:31

#3 楼

这可能只是一个偏好问题,但对我来说,让地图存储Maps的实例而不是直接存储描述似乎更有用,因为那样您还可以免费获得其他信息的功能(枚举项名称,其序数值等)。您可能不需要此功能,但是因为它非常简单,所以我肯定会认为更好。

#4 楼

对于此类,相对于它添加的复杂性,延迟初始化带来的好处太少。枚举中只有一个成员,并且枚举的每个成员构造起来都很简单。因此,您应该在类加载时使用静态初始化程序块填充映射。 (此外,它保证初始化仅发生一次,因此是线程安全的。)

public enum Maps {

    COLOR_RED("ABC", "abc description");

    private final String code;
    private final String description;

    private static final Map<String, String> MAP = new HashMap<String, String>();
    static {
        for (Maps s : Maps.values()) {
            MAP.put(s.code, s.description);
        }
    }

    private Maps(String code, String description) {
        this.code = code;
        this.description = description;
    }                                                                                                                               

    public String getCode() {                                                                                                       
        return code;                                                                                                                
    }                                                                                                                               

    public String getDescription() {                                                                                                
        return description;                                                                                                         
    }                                                                                                                               

    public static String getDescriptionByCode(String code) {                                                                        
        return MAP.get(code);
    }
}


此外,如果COLOR_RED没有任何有用的含义,并且代码字符串也是有效的Java标识符,请考虑将代码本身用作每个枚举成员的名称。枚举已经具有按名称查找成员的功能,因此您可以利用该机制。

public enum SimplerMap {                                                                                                            

    ABC("abc description");

    private final String description;                                                                                               

    private SimplerMap(String description) {                                                                                        
        this.description = description;                                                                                             
    }                                                                                                                               

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

    public String getDescription() {
        return this.description;                                                                                                    
    }                                                                                                                               

    public static String getDescriptionByCode(String code) {
        try {
            return valueOf(code).getCode();
        } catch (IllegalArgumentException noSuchCode) {
            return null;
        }
    }
}


#5 楼

首先,代码中的名称不是很清楚。
Maps是什么意思?为什么使用COLOR_RED以及为什么关联的代码ABC
枚举中只有一个值吗?

也许这只是示例代码,但这很难分析。

例如,如果您的值名为ABC,则只需调用Maps.valueOf("ABC")即可拥有关联的枚举并获取相应的描述,而无需内部映射。

当然,如果您需要ColorsCodes,都必须以某种方式将它们关联。但是,为什么还要使用字符串呢?也许您可以使用另一个称为Colors的枚举(定义COLOR_RED),以便仅使用枚举类型来完成颜色和代码之间的关系。

我已经有一段时间没有用Java编写了,但是如果我没记错的话,EnumMap s(例如,从ColorsCodes以及相反)非常有效(就像数组中的整数索引一样);而且,枚举的switch案例效率也更高,并提供了来自编译器的更多反馈:静态分析可以告诉您是否考虑了所有可能的枚举;这大大简化了您的代码,因为您无需费心进行错误检查。

当然,处理来自应用程序外部的数据时,字符串是必需的。但是在内部交换数据时,大多数情况下可以避免这种情况。

希望这会有所帮助。

#6 楼

只需在静态块中初始化不可变映射即可。
此外,放置lambda可以达到更高的目标:

private static final Map<String, String> mapCodes = new HashMap<>();     
static {
              final Map<String, String> items = EnumSet.allOf(Maps.class)
                    .stream()
                    /*.filter(// Can do some filtering excluding values)  */
                    .collect(Collectors.toMap(Maps::code, Maps::description));  

              mapCodes.putAll(items);
}


根据可重用性的级别,建议访问唯一值和Enum实例。 br />
.collect(Collectors.toMap(Maps::code, m-> m));


另外,在吸气方法中可以增强不变性,如:


番石榴:ImmutableMap.copyOf(mapCodes) //Recommended: Protects original map values of being mutated





Java集合:Collections.unmodifiableMap(mapCodes)