按值查找枚举的正确方法

时间:2011-02-03 13:54:53

标签: java enums

我有几个Java枚举,类似于下面的内容(为机密性编辑等)。 在每种情况下,我都有一个我真的不满意的查找方法;在下面的示例中,它是findByChannelCode

public enum PresentationChannel {
    ChannelA("A"),
    ChannelB("B"),
    ChannelC("C"),
    ChannelD("D"),
    ChannelE("E");

    private String channelCode;

    PresentationChannel(String channelCode) {
        this.channelCode = channelCode;
    }

    public String getChannelCode() {
        return this.channelCode;
    }

    public PresentationChannel findByChannelCode(String channelCode) {
        if (channelCode != null) {
            for (PresentationChannel presentationChannel : PresentationChannel.values()) {
                if (channelCode.equals(presentationChannel.getChannelCode())) {
                    return presentationChannel;
                }
            }
        }

        return null;
    }
}

问题是,当我可以使用HashMap<String, PresentationChannel>时,我觉得这些线性查找很傻。所以我想到了下面的解决方案,但是我希望它有点混乱,而且更重要的是,我肯定在别人遇到这个问题时我不在乎重新发明轮子。我想得到这个小组的一些圣人智慧:按值索引枚举的正确方法是什么?

我的解决方案:

ImmutableMap<String, PresentationChannel> enumMap = Maps.uniqueIndex(ImmutableList.copyOf(PresentationChannel.values()), new Function<PresentationChannel, String>() {
        public String apply(PresentationChannel input) {
            return input.getChannelCode();
        }});

并且,在枚举中:

public static PresentationChannel findByChannelCode(String channelCode) {
     return enumMap.get(channelCode);
}

7 个答案:

答案 0 :(得分:5)

我认为你在这里使用的是非JDK类吗?

使用JDK API的类似解决方案:

private static final Map<String, PresentationChannel> channels = new HashMap<String, PresentationChannel>();

static{
  for (PresentationChannel channel : values()){
    channels.put(channel.getChannelCode(), channel);
  }
}

答案 1 :(得分:5)

  

我想获得这个群体的一些圣人智慧:按值索引枚举的正确方法是什么?

很可能根本没有这样做

虽然哈希表提供O(1)查找,但它们也有相当大的常量开销(用于哈希计算等),因此对于小型集合,线性搜索可能更快(如果“有效方式”是您的定义) “正确的方式”)。

如果你只是想要一种干嘛的方法,我认为番石榴的Iterables.find是另一种选择:

return channelCode == null ? null : Iterables.find(Arrays.asList(values()),
    new Predicate<PresentationChannel>() {
        public boolean apply(PresentationChannel input) {
            return input.getChannelCode().equals(channelCode);
        }
    }, null);

答案 2 :(得分:3)

为什么不为成员A, B, C, D, E命名并使用valueOf

答案 3 :(得分:3)

我正在寻找类似的东西,并在this site找到一个简单,干净,笔直的方式。在枚举中创建并初始化静态最终地图,并为查找添加静态方法,因此它将类似于:

public enum PresentationChannel {
    ChannelA("A"),
    ChannelB("B"),
    ChannelC("C"),
    ChannelD("D"),
    ChannelE("E");

    private String channelCode;

    PresentationChannel(String channelCode) {
        this.channelCode = channelCode;
    }

    public String getChannelCode() {
        return this.channelCode;
    }

    private static final Map<String, PresentationChannel> lookup 
            = new HashMap<String, PresentationChannel>();

    static {
        for(PresentationChannel pc : EnumSet.allOf(PresentationChannel.class)) {
            lookup.put(pc.getChannelCode(), pc);
        }
    }

    public static PresentationChannel get(String channelCode) { 
        return lookup.get(channelCode); 
    }
}

答案 4 :(得分:2)

对于几个没关系的值,迭代遍历values array()。仅限一个注释:使用那样的smth。 values()在每次调用时克隆数组。

static final PresentationChannel[]  values=values(); 
static PresentationChannel getByCode(String code){
  if (code==null)
    return null;
  for(PresentationChannel channel: values) if (code.equals(channel.channelCode)) return channel;
  return null;
}

如果您有更多频道。

private static final Map<String code, PresentationChannel> map = new HashMap<String code, PresentationChannel>();
static{//hashmap sucks a bit, esp if you have some collisions so you might need to initialize the hashmap depending on the values count and w/ some arbitrary load factor
  for(PresentationChannel channel: values())  map.put(channel.channelCode, channel);
}

static PresentationChannel getByCode(String code){
  return map.get(code);
}

编辑:

所以实现一个帮助器接口,如下所示,另一个例子,为什么java语法泛型有时会爆炸 - 有时候最好不要使用。

用法PresentationChannel channel = EnumRepository.get(PresentationChannel.class, "A");
会有开销但很好,这很简单。

public interface Identifiable<T> {  
      T getId();    



    public static class EnumRepository{
      private static final ConcurrentMap<Class<? extends Identifiable<?>>, Map<?, ? extends Identifiable<?>>> classMap = new ConcurrentHashMap<Class<? extends Identifiable<?>>, Map<?,? extends Identifiable<?>>>(16, 0.75f, 1);

      @SuppressWarnings("unchecked")
      public static <ID, E extends Identifiable<ID>> E get(Class<E> clazz, ID value){
        Map<ID, E> map = (Map<ID, E>) classMap.get(clazz);
        if (map==null){
            map=buildMap(clazz);
            classMap.putIfAbsent(clazz, map);           
        }
        return map.get(value);
      }

      private static <ID, E extends Identifiable<ID>> Map<ID, E> buildMap( Class<E> clazz){
        E[] enumConsts = clazz.getEnumConstants();
        if (enumConsts==null)
            throw new IllegalArgumentException(clazz+ " is not enum");

        HashMap<ID, E> map = new HashMap<ID, E>(enumConsts.length*2);
        for (E e : enumConsts){
            map.put(e.getId(), e);
        }
        return map;
      }      
    }
}

enum X implements Identifiable<String>{
...
public String getId(){...}
}

次要警告:如果你把Identifiable放在那里,许多项目/ wepapp依赖它(并分享它)等等,就有可能泄漏类/类加载器。

答案 5 :(得分:1)

以下是实现不可修改地图的另一种方法:

protected static final Map<String, ChannelCode> EnumMap;
static { 
    Map<String, ChannelCode> tempMap = new HashMap<String, ChannelCode>();
    tempMap.put("A", ChannelA);
    tempMap.put("B", ChannelB);
    tempMap.put("C", ChannelC);
    tempMap.put("D", ChannelD);
    tempMap.put("E", ChannelE);
    EnumMap = Collections.unmodifiableMap(tempMap);
}

您可以使用EnumMap.get(someCodeAthroughE)快速检索ChannelCode。如果表达式为null,则表示找不到someCodeAthroughE

答案 6 :(得分:0)

如果您希望提供的channelCode始终有效,那么您可以尝试使用valueOf()方法获取枚举的正确实例。如果提供的值无效,则可以返回null或传播异常。

try {
    return PresentationChannel.valueOf(channelCode);
catch (IllegalArgumentException e) {
    //do something.
}