2011-02-03 9 views
11

Mam kilka wyliczeń Java, które wygląda mniej więcej poniżej (edytowane dla poufności, itp.). W każdym przypadku mam metodę wyszukiwania, z której naprawdę nie jestem zadowolony; w poniższym przykładzie jest to findByChannelCode.Właściwy sposób wyszukiwania wyliczenia według wartości

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

Problem polega na tym, czuję się głupio robi te liniowe wyszukiwań kiedy może być po prostu za pomocą HashMap<String, PresentationChannel>. Pomyślałem więc o rozwiązaniu poniżej, ale jest trochę bardziej zawikłane, że mam nadzieję i, co ważniejsze, nie chciałem ponownie wymyślać koła, gdy na pewno ktoś inny natknął się na to. Chciałem zdobyć trochę mądrości tej grupy: jaki jest właściwy sposób indeksowania wyliczenia według wartości?

Moje rozwiązanie:

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

, aw ENUM:

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

rozumiesz, że enumMap mapuje enum-> Object not vice verse? – bestsss

+0

Czy profilowanie kodu pokazuje, że wyszukiwanie liniowe jest niewystarczające? – trashgod

+0

@bestsss, może moja mapa jest źle nazwana. To nie jest EnumMap, tylko mapa z String-> PresentationChannel (tj. Value-> instance) – Ray

Odpowiedz

5

Chciałem zdobyć trochę mądrości mądrości tej grupy: jaki jest właściwy sposób indeksowania wyliczenia według wartości?

Całkiem możliwe nie robi w ogóle.

Podczas gdy tablice hash zapewniają wyszukiwanie o wartości O(1), mają również dość duży stały narzut (w przypadku obliczeń haszujących itp.), Więc w przypadku niewielkich kolekcji wyszukiwanie liniowe może być szybsze (jeśli "efektywna metoda" to twoja definicja " właściwy sposób").

Jeśli chcesz tylko suchą sposób to zrobić, przypuszczam Guava na Iterables.find jest alternatywą:

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

Dobre powoływanie się na "co mam na myśli przez" właściwe ". To niekoniecznie jest najbardziej wydajne. Jeśli jest niepotrzebny narzut, to nie jest dobrze. Jeśli jest podatny na błędy, to też nie jest dobry. Sądzę, że zastanawiałem się nad tym, co zrobi Josh Bloch? – Ray

+0

Tak, to prawdopodobnie całkiem dobra definicja "właściwej drogi". – gustafc

+0

@Ray, Joshua wysadził AbstarctCollection.toArray() i nowy ArrayList (Collection) w java 1.4, od tego czasu mają pewne spory. – bestsss

0

Jeśli spodziewasz dostarczonego channelCode aby zawsze być ważne, to możesz po prostu spróbować i uzyskać właściwą instancję enum za pomocą metody valueOf(). Jeśli podana wartość jest nieprawidłowa, możesz zwrócić wartość null lub propagować wyjątek.

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

Dlaczego nie nazwać swoich członków A, B, C, D, E i używać valueOf?

+0

, ponieważ mogą być podobne do tego 1,2,3,4 lub też Николас1, Николас2 itd., Tj. Nie są to identyfikatory java. – bestsss

+1

Wolałbym, aby nazwy nie były tak ściśle powiązane z wartościami. – Ray

5

Myślę, że tutaj używasz klas innych niż JDK, prawda?

Podobne rozwiązanie z JDK API:

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

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

Tak, używam Guava – Ray

2

dla kilku wartości, ok, iteracji przez tablicę wartości(). Jedna uwaga: użyj czegoś takiego. values() klonuje tablicę przy każdym wywołaniu.

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

jeśli masz więcej kanałów.

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:

Więc implementować interfejs pomocnika, jak pokazano poniżej, kolejny przykład dlaczego składni rodzajowych java ciosów i czasami lepiej - nie używany.

Użycie PresentationChannel channel = EnumRepository.get(PresentationChannel.class, "A");
Będzie nad głową, ale dobrze, to całkiem głupi dowód.

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(){...} 
} 

Minor ostrzeżenie: jeśli umieścisz identyfikowalne gdzieś tam, a wiele projektów/wepapp zależeć na nim (i udostępnić go) i tak dalej, to jest możliwe do wycieku klas/classloaders.

1

Oto kolejny sposób na wdrożenie niemodyfikowalne mapę:

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

Można użyć EnumMap.get(someCodeAthroughE) szybko odzyskać ChannelCode. Jeśli wyrażenie ma wartość NULL, nie znaleziono Twojego someCodeAthroughE.

3

szukałem czegoś podobnego i znaleźć na this site proste, czyste i prosto do punktu drogi . Utwórz i zainicjuj statyczną mapę końcową wewnątrz wyliczenia i dodaj statyczną metodę wyszukiwania, aby wyglądała następująco:

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

Zamiast używać 'EnumSet', możesz użyć tylko' PresentationChannel.values ​​() ' – pimlottc

Powiązane problemy