Правильный способ поиска перечисления по значению

158
15

У меня есть несколько перечислений 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);
}

спросил(а) 2021-01-19T15:49:02+03:00 9 месяцев, 1 неделя назад
1
Решение
112

Я хотел получить некоторую мудрость мудреца этой группы: каков правильный способ индексирования перечисления по значению?



Совершенно возможно вообще не делать этого.


В то время как хэш-таблицы обеспечивают поиск O(1), они также имеют довольно большие постоянные накладные расходы (для расчетов хэшей и т.д.), поэтому для небольших коллекций линейный поиск может быть быстрее (если "эффективный способ" - это ваше определение "правильный путь" ).

Если вы просто хотите использовать СУХОЙ способ, я полагаю, что Guava 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);

ответил(а) 2021-01-19T15:49:02+03:00 9 месяцев, 1 неделя назад
113

Я думаю, что вы используете не-JDK-классы здесь?


Аналогичное решение с API JDK:

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

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

ответил(а) 2021-01-19T15:49:02+03:00 9 месяцев, 1 неделя назад
92

Я искал что-то подобное и нашел на этот сайт, простой, чистый и прямолинейный. Создайте и инициализируйте статическую окончательную карту внутри вашего перечисления и добавьте статический метод для поиска, так что это будет что-то вроде:


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

ответил(а) 2021-01-19T15:49:02+03:00 9 месяцев, 1 неделя назад
92

Почему бы вам не назвать своих членов A, B, C, D, E и использовать valueOf?

ответил(а) 2021-01-19T15:49:02+03:00 9 месяцев, 1 неделя назад
79

для нескольких значений, которые соответствуют, итерации через 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);
}


Edit:


Итак, реализуйте вспомогательный интерфейс, как показано ниже, еще один пример, почему джоки синтаксиса 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 зависят от него (и обмениваются им) и т.д., возможно утечка классов/загрузчиков классов.

ответил(а) 2021-01-19T15:49:02+03:00 9 месяцев, 1 неделя назад
65

Вот еще один способ реализовать немодифицируемую карту:


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 не найден.

ответил(а) 2021-01-19T15:49:02+03:00 9 месяцев, 1 неделя назад
46

Если вы ожидаете, что предоставленный channelCode всегда будет действительным, вы можете просто попробовать и получить правильный экземпляр enum, используя метод valueOf(). Если предоставленное значение недействительно, вы можете вернуть значение null или распространить исключение.


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

ответил(а) 2021-01-19T15:49:02+03:00 9 месяцев, 1 неделя назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема