Pytanie Wyszukiwanie wyliczenia Java według wartości ciągu


Powiedz, że mam wyliczenie, które jest sprawiedliwe

public enum Blah {
    A, B, C, D
}

i chciałbym na przykład znaleźć wartość wyliczenia ciągu znaków "A" co byłoby Blah.A. Jak byłoby to możliwe?

Jest Enum.valueOf() jakiej metody potrzebuję? Jeśli tak, w jaki sposób mogę tego użyć?


1593
2018-03-02 22:56


pochodzenie




Odpowiedzi:


Tak, Blah.valueOf("A") da tobie Blah.A.

Zauważ, że nazwa musi być dokładny mecz, w tym przypadek: Blah.valueOf("a") i Blah.valueOf("A ") obaj rzucają IllegalArgumentException.

Metody statyczne valueOf() i values() są tworzone w czasie kompilacji i nie pojawiają się w kodzie źródłowym. Pojawiają się jednak w Javadoc; na przykład, Dialog.ModalityType pokazuje obie metody.


1858
2018-03-02 22:57



Dla odniesienia, Blah.valueOf("A") metoda jest wielkość liter i nie toleruje zewnętrznych białych znaków, a więc alternatywne rozwiązanie zaproponowane poniżej przez @ JoséMi. - Brett
@Michael Myers, skoro ta odpowiedź jest jak dotąd najbardziej głosowana, czy powinienem zrozumieć, że dobrą praktyką jest zdefiniowanie wyliczenia i jego wartości String, aby były dokładnie takie same? - Kevin Meredith
@KevinMeredith: Jeśli masz na myśli toString() wartość, nie, nie powiedziałbym tego. name() otrzyma aktualną zdefiniowaną nazwę stałej wyliczeniowej, chyba że ją przesłonisz. - Michael Myers♦
Co dokładnie masz na myśli przez "są tworzone w czasie kompilacji i nie pojawiają się w kodzie źródłowym." ? - treesAreEverywhere
@treesAreEverywhere Bardziej szczegółowo, te metody są wygenerowany (lub zsyntetyzowany) przez kompilator. Aktualny enum Blah {...} definicja nie powinna próbować deklarować własnych values ani valuesOf. To tak, jak możesz napisać "AnyTypeName.class", nawet jeśli nigdy nie zadeklarowałeś zmiennej "class"; kompilator sprawia, że ​​wszystko działa. (Ta odpowiedź może przestać być przydatna 3 miesiące później, ale na wszelki wypadek.) - Ti Strga


Inne rozwiązanie, jeśli tekst nie jest taki sam jak wartość wyliczeniowa:

public enum Blah {
  A("text1"),
  B("text2"),
  C("text3"),
  D("text4");

  private String text;

  Blah(String text) {
    this.text = text;
  }

  public String getText() {
    return this.text;
  }

  public static Blah fromString(String text) {
    for (Blah b : Blah.values()) {
      if (b.text.equalsIgnoreCase(text)) {
        return b;
      }
    }
    return null;
  }
}

711
2018-06-03 10:57



throw new IllegalArgumentException("No constant with text " + text + " found") byłoby lepsze niż return null. - whiskeysierra
@whiskeysierra Jon Skeet nie zgodziłby się z tym. stackoverflow.com/questions/1167982/... - Sanghyun Lee
@Sangdol Could ty oświeć nas, dlaczego powrót null jest lepszy? - whiskeysierra
@Sangdol zwykle dobrze jest sprawdzić, co SUN - oops - Oracle robi w tej samej sytuacji. I jako Enum.valueOf () pokazuje to JEST najlepsza praktyka, aby w tym przypadku rzucić wyjątek. Bo to wyjątkowa sytuacja. "Optymalizacja wydajności" jest złym pretekstem do napisania nieczytelnego kodu ;-) - raudi
Możesz też użyć adnotacji @Nullable, aby była "czytelna" ;-) - JoséMi


Oto sprytne narzędzie, którego używam:

/**
 * A common method for all enums since they can't have another base class
 * @param <T> Enum type
 * @param c enum type. All enums must be all caps.
 * @param string case insensitive
 * @return corresponding enum, or null
 */
public static <T extends Enum<T>> T getEnumFromString(Class<T> c, String string) {
    if( c != null && string != null ) {
        try {
            return Enum.valueOf(c, string.trim().toUpperCase());
        } catch(IllegalArgumentException ex) {
        }
    }
    return null;
}

Następnie w mojej klasie wyliczeniowej zazwyczaj mam to, aby zapisać niektóre pisanie:

public static MyEnum fromString(String name) {
    return getEnumFromString(MyEnum.class, name);
}

Jeśli twoje wyrazy nie są wszystkimi literami, po prostu zmień Enum.valueOf linia.

Szkoda, że ​​nie mogę tego użyć T.class dla Enum.valueOf tak jak T jest wymazany.


98
2017-11-12 15:52



Ten pusty blok catch naprawdę doprowadza mnie do szału, przepraszam. - whiskeysierra
@LazloBonin: Wyjątki dotyczą wyjątkowych warunków, a nie przepływu sterowania. Zdobądź kopię Efektywna Java. - Martin Schröder
Jeśli Java API, którego chcesz użyć, zgłasza wyjątek i nie chcesz, aby Twój kod go wyrzucił, możesz albo połknąć wyjątek w ten sposób, albo ponownie napisać logikę od podstaw, więc nie jest w ogóle zgłaszany wyjątek. Połknięcie wyjątku jest często mniejszym złem. - Nate C-K
Okropny! Zawsze, zawsze wychwytywanie wyjątków, w których możesz sobie z nimi poradzić. Powyższy przykład jest doskonałym przykładem jak tego NIE robić. Czemu? Więc zwraca NULL, a następnie wywołujący musi sprawdzić NULL lub wyrzucić NPE. Jeśli rozmówca wie, jak sobie poradzić z sytuacją, to wykonanie próby "vs" może wyglądać nieco bardziej elegancko, ALE jeśli nie może sobie poradzić, musi ponownie przekazać wartość zerową i dzwoniącemu osoby dzwoniącej jeszcze raz musi sprawdzić NULL, itp. itp. - raudi
Aby być sprawiedliwym w stosunku do powyższego rozwiązania, naprawdę istnieją przypadki wymagające zwracania wartości null zamiast odrzucania wyjątku IllegalArgumentException i przerwania przepływu programu, na przykład mapowania enum pomiędzy schematem usługi internetowej a schematem bazy danych, w którym nie zawsze są one jednym -do jednego. Zgadzam się jednak, że blok catch nigdy nie powinien być pusty. Umieść jakiś kod, na przykład log.warn lub coś w celu śledzenia. - Adrian M


Powinieneś także zachować ostrożność w swojej sprawie. Pozwól mi wyjaśnić: robienie Blah.valueOf("A") działa, ale Blah.valueOf("a") nie będzie działać. Potem znowu Blah.valueOf("a".toUpperCase(Locale.ENGLISH)) pracowałbym.

edytować
Zmieniono toUpperCase do toUpperCase(Locale.ENGLISH) oparte na tc. komentarz i java docs

edit2 Na Androida powinieneś użyć Locale.US, tak jak sulai wskazuje.


66
2018-05-09 16:33



Uważaj na domyślne ustawienia regionalne! - tc.
Dla was, użytkowników Androida, chciałbym to podkreślić Dokumentacja Androida wyraźnie zachęca do używania Locale.US dla danych wejściowych / wyjściowych do odczytu maszynowego. - sulai
Czy wielkie litery są różne w różnych miejscach? - Holloway
@Tngngot tak. - João Portela
@Tngngot Tak, niestety. Turcja jest dobrym przykładem. Połącz to z uszkodzoną obsługą domyślnych zestawów znaków Java w Javie (domyślnie na łacinie w systemie Windows zamiast Unicode), a prawie zawsze korzystanie z domyślnych wersji metod akceptujących zestaw znaków lub ustawienia narodowe jest prawie zawsze niebezpieczne. Powinieneś prawie zawsze jawnie je definiować. - Stijn de Witt


Użyj wzoru od Joshua Blocha, Efektywna Java:

(uproszczony dla zwięzłości)

enum MyEnum {
  ENUM_1("A"),
  ENUM_2("B");

  private String name;

  private static final Map<String,MyEnum> ENUM_MAP;

  MyEnum (String name) {
    this.name = name;
  }

  public String getName() {
    return this.name;
  }

  // Build an immutable map of String name to enum pairs.
  // Any Map impl can be used.

  static {
    Map<String,MyEnum> map = new ConcurrentHashMap<String,MyEnum>();
    for (MyEnum instance : MyEnum.values()) {
      map.put(instance.getName(),instance);
    }
    ENUM_MAP = Collections.unmodifiableMap(map);
  }

  public static MyEnum get (String name) {
    return ENUM_MAP.get(name);
  }
}

Zobacz także:

Przykład Oracle Java za pomocą Enum i Map of instances

Kolejność wykonywania bloków statycznych w typie Enum

Jak mogę wyszukać wyliczenie Java z jego wartości String


38
2018-06-15 16:37



jeśli Joshua Bloch powiedział to, to jest jedyny sposób, aby pójść :-). To wstyd, że zawsze muszę przewijać tutaj. - dermoritz
Jest to jeszcze prostsze w Javie 8, co możesz zrobić:Stream.of(MyEnum.values()).collect(toMap(Enum::name, identity())) Polecam także przesłonięcie toString () (przekazywane przez konstruktor) i używanie go zamiast nazwy, szczególnie jeśli Enum jest powiązane z danymi szeregowalnymi, ponieważ pozwala to kontrolować obudowę bez dopasowania Sonaru. - Novaterata
Java 8 z pewnością może / zmieni wiele (lepszych) odpowiedzi na tym forum. Nie jestem pewien czy kiedykolwiek mieć ogon (Sonar) machać psem (kod aplikacji). - Darrell Teague
Jeśli masz zamiar umieścić to w unmodifiableMap tak czy inaczej, nie ma korzyści, aby zacząć od a ConcurrentHashMap. Po prostu użyj a HashMap. (Jeśli masz Guava ImmutableMap to polecam zamiast tego!) - Daniel Pryden
ImmostableMap firmy Google to z pewnością lepsza (prawdziwa, głęboko niezmienna) implementacja, ale w celu demonstracji rozwiązania opartego wyłącznie na jądrze Java ... i skupienia się na przypadku wyszukiwania String String, wykorzystano jedynie klasy core-Java. Ogólnie rzecz biorąc, ConcurrentHashMap jest prawie zawsze preferowany w stosunku do HashMap, biorąc pod uwagę ogólny profil wydajności (dodawanie, usuwanie, modyfikowanie) i prostotę w porównaniu z zawijaniem / blokowaniem HashMap, gdy konieczne jest rozważenie jednoczesnej modyfikacji. - Darrell Teague


Oto metoda, która może to zrobić dla dowolnego Enum, i jest niewrażliwa na wielkość liter.

/** 
 * Finds the value of the given enumeration by name, case-insensitive. 
 * Throws an IllegalArgumentException if no match is found.  
 **/
public static <T extends Enum<T>> T valueOfIgnoreCase(
        Class<T> enumeration, String name) {

    for (T enumValue : enumeration.getEnumConstants()) {
        if (enumValue.name().equalsIgnoreCase(name)) {
            return enumValue;
        }
    }

    throw new IllegalArgumentException(String.format(
        "There is no value with name '%s' in Enum %s",
        name, enumeration.getName()
    ));
}

34
2017-12-01 21:53



Ta odmiana robi to poprawnie: equalsIgnoreCase jest drogą do zrobienia. +1 - Stijn de Witt


Za pomocą Blah.valueOf(string) jest najlepszy, ale możesz go użyć Enum.valueOf(Blah.class, string) także.


28
2018-05-09 17:23



Uwzględniaj wielkość liter, nie pomagaj! - Murtaza Kanchwala
@MurtazaKanchwala Czy możesz wyjaśnić swój komentarz? Co próbujesz zrobić? - Peter Lawrey
Hi @ PeterLawrey, Próbowałem pobrać Enum from a String publiczna enum ObjectType {PERSON ("Person") public String parameterName; ObjectType (String parameterName) {this.parameterName = parameterName; } public String getParameterName () {return this.parameterName; } public static ObjectType fromString (String parameterName) {if (parameterName! = null) {for (ObjectType objType: ObjectType.values ​​()) {if (parameterName.equalsIgnoreCase (objType.parameterName)) {return objType; }}} zwraca wartość null; }} - Murtaza Kanchwala