Java: Sprawdź, czy enum zawiera podany ciąg?


169

Oto mój problem - szukam (jeśli w ogóle istnieje) odpowiednika enum ArrayList.contains();.

Oto próbka mojego problemu z kodem:

enum choices {a1, a2, b1, b2};

if(choices.???(a1)}{
//do this
} 

Teraz zdaję sobie sprawę, że opcja ArrayListof Stringsbyłaby tutaj lepszą trasą, ale muszę uruchomić zawartość wyliczenia za pomocą przełącznika / przypadku w innym miejscu. Stąd mój problem.

Zakładając, że coś takiego nie istnieje, jak mam się do tego zabrać?


Przełącznik / przypadek ze stringami jest zaimplementowany począwszy od Java 7
AndreyP

Odpowiedzi:


205

To powinno wystarczyć:

public static boolean contains(String test) {

    for (Choice c : Choice.values()) {
        if (c.name().equals(test)) {
            return true;
        }
    }

    return false;
}

W ten sposób nie musisz się martwić o późniejsze dodanie dodatkowych wartości wyliczenia, wszystkie są sprawdzane.

Edycja: Jeśli wyliczenie jest bardzo duże, możesz umieścić wartości w HashSet:

public static HashSet<String> getEnums() {

  HashSet<String> values = new HashSet<String>();

  for (Choice c : Choice.values()) {
      values.add(c.name());
  }

  return values;
}

Następnie możesz po prostu zrobić: values.contains("your string")co zwraca prawdę lub fałsz.


12
to bardzo słaba impl .: Choice.valueOf (test) jest tym, czego chcesz (w / try / catch)
bestsss

17
bestsss, jest to zdecydowanie najbardziej odpowiednie rozwiązanie. Zgłaszanie wyjątku w celu zaimplementowania metody exist () jest złą praktyką. Chociaż możesz pomyśleć, że Twoja implementacja jest bardziej wydajna, ponieważ nie wygląda na O (n), znajduje się ona w podstawowej strukturze, która nie jest widoczna. Również użycie try {} catch zwiększa obciążenie. Poza tym to po prostu nieładne.
jluzwick

25
@Jared, zdecydowanie valueOf. Po prostu złap wyjątek i zwróć false. Dla tych, którzy mówią inaczej, jeśli spojrzeć na implementację, używa ona już mapy, a programiści JDK mają znacznie większą szansę na jej optymalizację. API zgłasza wyjątek, co jest dyskusyjną praktyką (zamiast zwracać wartość null), ale kiedy masz do czynienia z interfejsem API, który zgłasza wyjątek, idź z nim, nie wynajduj koła na nowo.
Yishai

2
@jluzwick try / catch overhead jest prostą instrukcją skoku, gdy nie jest pobierana, a nie używanie wyjątku w bloku catch jest również zoptymalizowane. Strach przed próbą złapania powodu utraty wydajności jest złą praktyką.
bestsss

22
zawiera () ma mieć pierwszeństwo przed valueOf () z wyjątkiem. Czemu? Ponieważ „wyjątki, jak sama nazwa wskazuje, mają być używane tylko w wyjątkowych warunkach; nigdy nie powinny być używane w zwykłym przepływie sterowania” (Joshua Bloch, „Efektywna Java”).
james.garriss

226

Zamiast tego użyj biblioteki Apache commons lang3

 EnumUtils.isValidEnum(MyEnum.class, myValue)

33
Uwaga dla zainteresowanych: podstawową implementacją, której użyli, jest po prostu rozwiązanie try / catch (@since 3.0 @version $ Id: EnumUtils.java 1199894 2011-11-09 17: 53: 59Z ggregory $).
Jonathan Gawrych

1
Upraszcza mój kod, więc nie obchodzi mnie, czy używa wyjątku do kontroli przepływu (naprawdę nie powinni) ... Byłoby miło, gdyby to zmienili.
jpangamarca


1
Czy guawa zawiera również takie rozwiązanie?
Cypress Frankenfeld

50

Możesz użyć Enum.valueOf()

enum Choices{A1, A2, B1, B2};

public class MainClass {
  public static void main(String args[]) {
    Choices day;

    try {
       day = Choices.valueOf("A1");
       //yes
    } catch (IllegalArgumentException ex) {  
        //nope
  }
}

Jeśli spodziewasz się, że sprawdzanie często kończy się niepowodzeniem, lepiej byłoby użyć prostej pętli, jak pokazały inne - jeśli twoje wyliczenia zawierają wiele wartości, być może builda HashSetlub coś podobnego z twoich wartości wyliczeniowych przekonwertowanych na ciąg i zapytaj o to HashSet.


8
Nie sądzę, by w takim przypadku najlepszym wyborem był wyjątek.
GokcenG

6
Try and Catch powinno być ostatecznością. Try and Catch są zbyt drogie
Jesus Dimrix

2
poleganie na wyjątkach czasu wykonywania w celu wykonywania logiki biznesowej, oprócz drogich, nie jest tak czytelne. jeśli chodzi o sprawdzane wyjątki, jest inaczej, ponieważ są one częścią biznesu.
Luís Soares

2
Zapobiega to również włączaniu szerokiej przerwy w wyrzucaniu wyjątków w celu znalezienia rzeczywistych wyjątkowych przypadków, które są ponawiane. (lub przynajmniej sprawia, że ​​jest to bardzo irytujące). Używaj wyjątków w wyjątkowych przypadkach.
Nickolay Kondratyev

4
EnumUtils.isValidEnum(MyEnum.class, myValue)używa podobnej logiki i IMO, warto dodać całą bibliotekę do tego trywialnego zadania
Vikramjit Roy

37

Jeśli używasz Java 1.8, możesz wybrać Stream + Lambda, aby zaimplementować to:

public enum Period {
    DAILY, WEEKLY
};

//This is recommended
Arrays.stream(Period.values()).anyMatch((t) -> t.name().equals("DAILY1"));
//May throw java.lang.IllegalArgumentException
Arrays.stream(Period.values()).anyMatch(Period.valueOf("DAILY")::equals);

19

Nawet lepiej:

enum choices {
   a1, a2, b1, b2;

  public static boolean contains(String s)
  {
      for(choices choice:values())
           if (choice.name().equals(s)) 
              return true;
      return false;
  } 

};

Dzięki za optymistyczne rozwiązanie
Parth Patel

19

Guavas Enums może być twoim przyjacielem

Na przykład to:

enum MyData {
    ONE,
    TWO
}

@Test
public void test() {

    if (!Enums.getIfPresent(MyData.class, "THREE").isPresent()) {
        System.out.println("THREE is not here");
    }
}

11

Możesz najpierw przekonwertować wyliczenie na List, a następnie użyć metody list zawiera

enum Choices{A1, A2, B1, B2};

List choices = Arrays.asList(Choices.values());

//compare with enum value 
if(choices.contains(Choices.A1)){
   //do something
}

//compare with String value
if(choices.contains(Choices.valueOf("A1"))){
   //do something
}

To powinna być akceptowana odpowiedź. Konwersja na listę to najczystszy sposób na zrobienie tego. Oszczędza on całą (boczną) dyskusję na temat „właściwego używania wyjątków” w Javie w innych odpowiedziach tutaj.
Manuel

Zgłoś IllegalArgumentException, jeśli wartość nie istnieje.
mkyong

10

Wspomniano tutaj o kilku bibliotekach, ale tęsknię za tą, której tak naprawdę szukałem: Wiosna!

Istnieje ObjectUtils # includesConstant, która domyślnie nie rozróżnia wielkości liter, ale może być ścisła, jeśli chcesz. Jest używany w następujący sposób:

if(ObjectUtils.containsConstant(Choices.values(), "SOME_CHOISE", true)){
// do stuff
}

Uwaga: użyłem tutaj przeciążonej metody, aby zademonstrować, jak używać sprawdzania wielkości liter. Możesz pominąć wartość logiczną, aby zachowanie nie uwzględniało wielkości liter.

Uważaj jednak na duże wyliczenia, ponieważ nie używają implementacji Map, jak niektórzy ...

Jako bonus zapewnia również wariant valueOf: ObjectUtils # caseInsensitiveValueOf niewrażliwy na wielkość liter


9

Kilka założeń:
1) Brak try / catch, ponieważ jest to wyjątkowa kontrola przepływu
2) Metoda „zawiera” musi być szybka, ponieważ zwykle jest wykonywana kilka razy.
3) Przestrzeń nie jest ograniczona (typowe dla zwykłych rozwiązań)

import java.util.HashSet;
import java.util.Set;

enum Choices {
    a1, a2, b1, b2;

    private static Set<String> _values = new HashSet<>();

    // O(n) - runs once
    static{
        for (Choices choice : Choices.values()) {
            _values.add(choice.name());
        }
    }

    // O(1) - runs several times
    public static boolean contains(String value){
        return _values.contains(value);
    }
}

7

Możesz tego użyć

YourEnum {A1, A2, B1, B2}

boolean contains(String str){ 
    return Sets.newHashSet(YourEnum.values()).contains(str);
}                                  

Aktualizacja sugerowana przez @ wightwulf1944 została uwzględniona, aby zwiększyć wydajność rozwiązania.


4
Ta implementacja jest nieefektywna. Powoduje to iterację wartości wyliczenia w celu utworzenia nowego zestawu, a następnie utworzenie strumienia, który wykonuje iterację w wynikowym zestawie. Oznacza to, że nowy zestaw i strumień jest tworzony za każdym razem, gdy wywoływana jest funkcja, a użycie stream()na zestawie oznacza, że ​​wykonujesz iterację na każdym elemencie w zestawie, zamiast korzystać z bazowej tablicy hashy, która będzie szybsza. Aby to poprawić, najlepiej buforować utworzony Zestaw i contains()zamiast tego użyć jego metody. Jeśli musisz pobrać strumień, użyj Arrays.stream()zamiast tego.
Subaru Tashiro

3

Myślę, że tak nie jest, ale możesz zrobić coś takiego:

enum choices {a1, a2, b1, b2};

public static boolean exists(choices choice) {
   for(choice aChoice : choices.values()) {
      if(aChoice == choice) {
         return true;
      }
   }
   return false;
}

Edytować:

Zapoznaj się z wersją Richarda, ponieważ jest bardziej odpowiednia, ponieważ nie zadziała, chyba że przekonwertujesz go na użycie ciągów, co robi Richards.


ale myślę, że OP chce przetestować ciąg?
Richard H

tak haha. Ta metoda nie byłaby tak skuteczna, jak już wiemy, że wybór znajduje się w wyliczeniach. Twoja modyfikacja jest bardziej poprawna.
jluzwick

1
Jeśli chcesz popracować nad jakimś podzbiorem samych wyliczeń (a nie ich nazwami), lepiej przyjrzyj się EnumSet. download.oracle.com/javase/6/docs/api/java/util/EnumSet.html
Yishai,

Może to głupie pytanie, ale dlaczego nie jest .values()udokumentowane na download.oracle.com/javase/6/docs/api/java/lang/Enum.html ?
Anonim

To świetne pytanie. Masz rację, nie ma go w dokumentacji ani w źródle Enum. Zakładam, że jest obecny w jednej z implementacji Enum lub jest jakaś reguła JLS, która na to pozwala. Również wszystkie obiekty Collection mają to i można to traktować jako Collection, mimo że niekoniecznie implementuje Collection.
jluzwick

3

Strumienie Java zapewniają elegancki sposób na zrobienie tego

Stream.of(MyEnum.values()).anyMatch(v -> v.name().equals(strValue))

Zwraca: prawda, jeśli którykolwiek element strumienia pasuje do podanej wartości, w przeciwnym razie fałsz


2

Dlaczego nie połączyć odpowiedzi Pabla z wartością valueOf ()?

public enum Choices
{
    a1, a2, b1, b2;

    public static boolean contains(String s) {
        try {
            Choices.valueOf(s);
            return true;
        } catch (Exception e) {
            return false;
        }
}

Proszę nie. Zobacz inną, starszą odpowiedź, która jest odpowiednikiem Twojej: stackoverflow.com/a/4936872/103412
Torsten

1

To podejście można wykorzystać do sprawdzenia dowolnego Enum, możesz dodać go do Utilsklasy:

public static <T extends Enum<T>> boolean enumContains(Class<T> enumerator, String value)
{
    for (T c : enumerator.getEnumConstants()) {
        if (c.name().equals(value)) {
            return true;
        }
    }
    return false;
}

Użyj tego w ten sposób:

boolean isContained = Utils.enumContains(choices.class, "value");

1

Stworzyłem następną klasę do tej walidacji

public class EnumUtils {

    public static boolean isPresent(Enum enumArray[], String name) {
        for (Enum element: enumArray ) {
            if(element.toString().equals(name))
                return true;
        }
        return false;
    }

}

przykład użycia:

public ArrivalEnum findArrivalEnum(String name) {

    if (!EnumUtils.isPresent(ArrivalEnum.values(), name))
        throw new EnumConstantNotPresentException(ArrivalEnum.class,"Arrival value must be 'FROM_AIRPORT' or 'TO_AIRPORT' ");

    return ArrivalEnum.valueOf(name);
}

0

Możesz użyć, valueOf("a1")jeśli chcesz wyszukać ciąg znaków


3
ale to nie jest eleganckie… jeśli wartość nie istnieje, generuje wyjątek. więc prawdopodobnie musisz go otoczyć próbą catch
Kasturi

To spowoduje wyjątek, jeśli wartość nie istnieje
Richard H

Mniej eleganckie niż przechodzenie przez opcje wyliczenia w poszukiwaniu pasującego obiektu?
jprete

0

Jest to wyliczenie, są to wartości stałe, więc jeśli jest w instrukcji przełącznika, robi coś takiego:

case: val1
case: val2

Dlaczego miałbyś wiedzieć, co jest zadeklarowane jako stała?


Tego fragmentu kodu nie ma we wspomnianej instrukcji switch, to jest gdzie indziej. Po prostu stwierdziłem, że w tym przypadku wyliczenie jest konieczne, ponieważ inni sugerowali, że zamiast tego używam ArrayList.
Jared,

@Jared ma teraz znacznie więcej sensu
Woot4Moo

@Jared to jednak nie ma znaczenia, ponieważ znasz już wartości, które tam są. Zasadniczo odpowiednikiem wyliczenia list.contains () jest MyEnum.MyAwesomeValue
Woot4Moo

0

Z guawą jest to jeszcze prostsze:

boolean isPartOfMyEnum(String myString){

return Lists.newArrayList(MyEnum.values().toString()).contains(myString);

}

Jak powiedziała Kioria , to nie zadziała. MyEnum.values()zwraca tablicę instancji MyEnum i MyEnum.value().toString()zwraca reprezentację ciągu tego obiektu tablicy (po prostu ciąg znaków, taki jak "[LMyEnum; @ 15b94ed3")
user2137020

Musisz wywołać .name()zamiast .toString()(chyba że zastąpisz domyślną metodę toString). Zobacz to, aby uzyskać więcej informacji: Różnica między enum .name () i .toString ()
Gaʀʀʏ

0

Łączy to wszystkie podejścia z poprzednich metod i powinno mieć równoważną wydajność. Może być używany dla dowolnego wyliczenia, wbudowuje rozwiązanie „Edytuj” z @Richard H i używa wyjątków dla nieprawidłowych wartości, takich jak @bestsss. Jedyny kompromis polega na tym, że należy określić klasę, ale to zmienia to w dwuliniowiec.

import java.util.EnumSet;

public class HelloWorld {

static enum Choices {a1, a2, b1, b2}

public static <E extends Enum<E>> boolean contains(Class<E> _enumClass, String value) {
    try {
        return EnumSet.allOf(_enumClass).contains(Enum.valueOf(_enumClass, value));    
    } catch (Exception e) {
        return false; 
    }
}

public static void main(String[] args) {
    for (String value : new String[] {"a1", "a3", null}) {
        System.out.println(contains(Choices.class, value));
    }
}

}


0
com.google.common.collect.Sets.newHashSet(MyEnum.values()).contains("myValue")

0

rozwiązanie, aby sprawdzić, czy wartość jest obecna, a także uzyskać wartość wyliczenia w zamian:

protected TradeType getEnumType(String tradeType) {
    if (tradeType != null) {
        if (EnumUtils.isValidEnum(TradeType.class, tradeType)) {
            return TradeType.valueOf(tradeType);
        }
    }
    return null;
}

0

Ten działa dla mnie:

Arrays.asList(YourEnum.values()).toString().contains("valueToCheck");

3
Twoja wersja zwróci wartość true, nawet jeśli YourEnum zawiera „valueToCheckBlaBla”, ponieważ „valueToCheck” będzie obecny w ciągu reprezentującym całą listę.
Nicko,

0

Jeśli używasz Java 8 lub nowszej, możesz to zrobić:

boolean isPresent(String testString){
      return Stream.of(Choices.values()).map(Enum::name).collect(Collectors.toSet()).contains(testString);
}


0

Możesz zrobić to jako metodę zawierającą:

enum choices {a1, a2, b1, b2};
public boolean contains(String value){
    try{
        EnumSet.allOf(choices.class).contains(Enum.valueOf(choices.class, value));
        return true;
    }catch (Exception e) {
        return false;
    }
}

lub możesz po prostu użyć go ze swoim blokiem kodu:

try{
    EnumSet.allOf(choices.class).contains(Enum.valueOf(choices.class, "a1"));
    //do something
}catch (Exception e) {
    //do something else
}

0

możesz też użyć: com.google.common.base.Enums

Enums.getIfPresent (varEnum.class, varToLookFor) zwraca opcjonalny

Enums.getIfPresent (fooEnum.class, myVariable) .isPresent ()? Enums.getIfPresent (fooEnum.class, myVariable) .get: fooEnum.OTHERS


0

Po prostu napisałbym

Arrays.stream(Choice.values()).map(Enum::name).collect(Collectors.toList()).contains("a1");

Enum # equals działa tylko w przypadku porównania obiektów.


-1
public boolean contains(Choices value) {
   return EnumSet.allOf(Choices.class).contains(value);
}

to nie zadziała. set mają obiekty wyliczeniowe, podczas gdy sprawdzasz, czy nie ma ciągu.
iTake

teraz odpowiedź nie pasuje do pytania, bo chodziło o String :)
iTake

-11

enumsą dość potężne w Javie. Możesz łatwo dodać containsmetodę do swojego wyliczenia (tak jak dodasz metodę do klasy):

enum choices {
  a1, a2, b1, b2;

  public boolean contains(String s)
  {
      if (s.equals("a1") || s.equals("a2") || s.equals("b1") || s.equals("b2")) 
         return true;
      return false;
  } 

};

miałeś na myśli s.equals("b1") || s.equals("b2")??
Jigar Joshi

3
Prawdopodobnie nie będzie to najlepszy sposób, aby to zrobić, ponieważ będziesz musiał dodać nowe s.equals("xx")dla każdego wyliczenia, które dodasz później.
jluzwick

1
Będzie ponad 1000 wyliczeń.
Jared

20
W jaki sposób ludzie, którzy sugerują okropne rozwiązania, takie jak to, uzyskują reputację 64K? Nienawidzę myśleć o całym tym kiepskim kodzie, który jest rozrzucany w oparciu o odpowiedzi tych współpracowników
Dexygen
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.