Java: Enum'un belirli bir dize içerip içermediğini kontrol ettiniz mi?


169

İşte benim sorunum - (varsa bile) enum eşdeğerini arıyorum ArrayList.contains(); .

İşte benim kod sorunu bir örnek:

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

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

Şimdi, bir fark ArrayListait StringsBurada daha iyi rota olacağı ama başka bir yerde bir switch / örneği üzerinden benim enum içeriğini yayınlamak zorunda. Bu yüzden benim sorunum.

Böyle bir şeyin olmadığını varsayarsak, bunu nasıl yapabilirim?


Dizeli anahtar / kasa, Java 7
AndreyP'den

Yanıtlar:


206

Bunu yapmalı:

public static boolean contains(String test) {

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

    return false;
}

Bu şekilde, daha sonra ek numaralandırma değerleri ekleme konusunda endişelenmenize gerek olmadığı anlamına gelir, hepsi kontrol edilir.

Düzenleme: Numaralandırma çok büyükse bir HashSet değerleri yapışabilir:

public static HashSet<String> getEnums() {

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

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

  return values;
}

Sonra sadece şunu yapabilirsiniz: values.contains("your string")doğru veya yanlış döndürür.


12
bu çok kötü bir impl .: Seçim.valueOf (test) istediğin şey (w / dene / yakala)
bestsss

17
bestsss, bu açıkça en uygun çözümdür. Exist () yönteminin türünü uygulamak için bir istisna atmak kötü bir uygulamadır. Uygulamanızın O (n) 'ye bakmadığı için daha verimli olduğunu düşünebilirsiniz, ancak görünmeyen temel çerçevede. Ayrıca try {} catch kullanarak ek yük ekler. Artı, sadece hoş değil.
jluzwick

25
@Kızan, kesinlikle valueOf. İstisnayı yakala ve yanlış döndür. Aksi söyleyenler, uygulamaya bakarsanız, zaten bir Harita kullanır ve JDK geliştiricileri bunu optimize etmek için çok daha iyi bir şansa sahiptir. API, tartışmalı bir uygulama olan bir istisna atar (null döndürmek yerine), ancak bir istisna atan bir API ile uğraşırken onunla devam edin, tekerleği yeniden icat etmeyin.
Yishai

2
@jluzwick tepegöz dene / yakala, alınmadığında basit bir atlama talimatıdır, catch bloğunda İstisna kullanmamak da optimize edilmiştir. Performans kaybının neden olduğu korkma denemesi / yakalaması kötü bir uygulamadır.
bestsss

23
içeren () bir istisna dışında valueOf () yerine tercih edilir. Neden? "İstisnalar, adından da anlaşılacağı gibi, yalnızca istisnai koşullar için kullanılmalıdır; asla normal kontrol akışı için kullanılmamalıdır" (Joshua Bloch, "Etkili Java").
james.garriss

226

Bunun yerine Apache commons lang3 lib kullanın

 EnumUtils.isValidEnum(MyEnum.class, myValue)

33
İlgilenenler için, kullandıkları temel uygulama basitçe dene / yakala çözümüdür (since 3.0 @ sürüm $ Id: EnumUtils.java 1199894 2011-11-09 17: 53: 59Z ggregory $).
Jonathan Gawrych

1
Kodumu kolaylaştırır, bu yüzden akış kontrolü (gerçekten olmamalıdır) için istisna kullanırsa umurumda değil ... Bunu değiştirdiler güzel olurdu.
jpangamarca


1
Guava da bunun gibi bir çözüm içeriyor mu?
Cypress Frankenfeld

51

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

Kontrolün sık sık başarısız olmasını bekliyorsanız, diğerlerinin gösterdiği gibi basit bir döngü kullanmaktan daha iyi olabilirsiniz - numaralarınız birçok değer içeriyorsa , belki de builda HashSetveya enum değerlerinizin bir dizeye dönüştürülmüş ve buna benzer bir sorgu HashSet.


8
Böyle bir durumda İstisna'nın en iyi seçim olduğunu düşünmüyorum.
GokcenG

6
Deneyin ve yakalamak son çare olmalıdır. Try and Catch pahalı
Jesus Dimrix

2
iş mantığı yapmak için çalışma zamanı istisnalarına güvenmek, pahalı olmasının yanı sıra, okunabilir değildir. işaretli istisnalarla ilgili olarak, farklıdır, çünkü bunlar işin bir parçasını oluşturur.
Luís Soares

2
Bu aynı zamanda herkesin yeniden denenmekte olan istisnai durumları bulmak için atılan istisnalar üzerinde geniş bir ara vermesini önler. (veya en azından bunu çok sinir bozucu yapar). İstisnai durumlar için istisnalar kullanın.
Nickolay Kondratyev

4
EnumUtils.isValidEnum(MyEnum.class, myValue)benzer bir mantık kullanır ve IMO bu önemsiz görev için bir kütüphane eklemek mantıklı
Vikramjit Roy

37

Java 1.8 kullanıyorsanız, bunu uygulamak için Stream + Lambda'yı seçebilirsiniz:

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

20

Guavas Enums arkadaşın olabilir

Örneğin bunun gibi:

enum MyData {
    ONE,
    TWO
}

@Test
public void test() {

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

19

Daha iyi:

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

};

İyimser çözüm için teşekkürler
Parth Patel

11

Önce numaralandırmayı List'e dönüştürebilir ve sonra list include yöntemini kullanabilirsiniz.

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
}

Bu kabul edilen cevap olmalı. Listeye dönüşüm, bunu yapmanın en temiz yoludur. Buradaki diğer yanıtlarda Java'daki "istisnaların uygun kullanımı" hakkındaki tüm tartışmayı yedekler.
Manuel

Değer yoksa IllegalArgumentException öğesini atın.
mkyong

10

Birkaç varsayım:
1) Deneme / yakalama yok, istisnai akış kontrolü olduğundan
2) 'içerir' yöntemi genellikle birkaç kez çalıştığı için hızlı olmalıdır.
3) Alan sınırlı değildir (sıradan çözümler için ortaktır)

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

10

Burada birkaç kütüphaneden bahsedildi, ama aslında aradığım kütüphaneyi özledim: Bahar!

Varsayılan olarak büyük / küçük harfe duyarlı olmayan ObjectUtils # includeConstant vardır , ancak isterseniz katı olabilir. Bu şekilde kullanılır:

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

Not: Büyük / küçük harfe duyarlı kontrolün nasıl kullanılacağını göstermek için burada aşırı yüklenmiş yöntemi kullandım. Büyük / küçük harfe duyarlı olmayan davranışa sahip olmak için boolean'ı atlayabilirsiniz.

Bazı uygulamalarda olduğu gibi Harita uygulamasını kullanmadıkları için büyük numaralara dikkat edin ...

Bonus olarak, valueOf'un büyük / küçük harfe duyarlı olmayan bir varyantını da sağlar: ObjectUtils # caseInsensitiveValueOf


7

Bunu kullanabilirsiniz

Numaram {A1, A2, B1, B2}

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

@ Wightwulf1944 tarafından önerilen güncelleme, çözümü daha verimli hale getirmek için dahil edilmiştir.


4
Bu uygulama verimsiz. Bu, yeni bir küme oluşturmak için enum değerleri üzerinden yinelenir, ardından ortaya çıkan kümede yinelenen bir akış oluşturur. Bu, işlev her çağrıldığında yeni bir Küme ve Akış oluşturulduğu stream()anlamına gelir ve bir kümede kullanmak , daha hızlı olacak olan alt satırdan yararlanmak yerine kümedeki her öğeyi yinelediğiniz anlamına gelir. Bunu iyileştirmek için, oluşturulan Kümeyi önbelleğe almak ve contains()bunun yerine yöntemini kullanmak en iyisidir . Bir akış almanız gerekiyorsa,Arrays.stream() bunun yerine kullanın.
Subaru Tashiro

3

Ben orada olduğunu sanmıyorum, ama böyle bir şey yapabilirsiniz:

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

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

Düzenle:

Richard'ın yaptığı Dizeleri kullanmaya dönüştürmediğiniz sürece bu işe yaramayacağı için lütfen Richard'ın bu sürümüne bakın.


ama OP bir dizeyi test etmek istediğini düşünüyorum?
Richard H

Evet haha. Bu yöntem, seçimin enumlarda olduğunu zaten bildiğimiz kadar etkili olmaz. Değişikliğiniz daha doğru.
jluzwick

1
Numaraların kendilerinin (alt adları değil) bazı alt kümeleri üzerinde çalışmak istiyorsanız, EnumSet'e bakmak daha iyidir. download.oracle.com/javase/6/docs/api/java/util/EnumSet.html
Yishai

Belki aptalca bir soru, ama download.oracle.com/javase/6/docs/api/java/lang/Enum.html.values() adresinde neden belgelenmiyor ?
Anonim

Bu harika bir soru. Haklısın dokümantasyonda ve Enum kaynağında da yok. Enum uygulamalarından birinde mevcut olduğunu veya buna izin veren bazı JLS kuralı olduğunu varsayıyorum. Ayrıca tüm Koleksiyon nesneleri buna sahiptir ve Koleksiyon uygulaması zorunlu olmasa da bu Koleksiyon olarak görülebilir.
jluzwick

3

Java Streams bunu yapmak için zarif bir yol sunar

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

Döndürür: akıştaki herhangi bir öğe sağlanan değerle eşleşirse true, aksi takdirde false


2

Neden Pablo'nun cevabını bir valueOf () ile birleştirmiyorsunuz?

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

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

Lütfen yapma. Sizinkiyle eşdeğer olan diğer, daha eski cevabı görün: stackoverflow.com/a/4936872/103412
Torsten

1

Bu yaklaşım herhangi birini kontrol etmek için kullanılabilir Enum, bir Utilssınıfa ekleyebilirsiniz :

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

Bu şekilde kullanın:

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

1

Bu doğrulama için bir sonraki sınıfı oluşturdum

public class EnumUtils {

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

}

kullanım örneği:

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

valueOf("a1")String ile aramak istiyorsanız kullanabilirsiniz


3
ama bu zarif değil .. değer yoksa bir istisna atar. bu yüzden muhtemelen catch ile çevrelemelisiniz
Kasturi

Değer yoksa bir istisna atar
Richard H

Eşleşen bir nesne arayan numaralandırma seçeneklerinde yinelemekten daha az zarif mi?
jprete

0

Bir enum, bunlar sabit değerlerdir, bu yüzden bir switch deyiminde sadece böyle bir şey yapıyorsa:

case: val1
case: val2

Ayrıca neyin sabit olarak beyan edildiğini neden bilmek istesin ki?


Bu kod biti anahtar deyiminde değil, başka bir yerde. Basitçe, bu durumda enum'un gerekli olduğunu belirtiyordum, çünkü diğerleri bunun yerine bir ArrayList kullandığımı önerdi.
Jared

@Jared bu şimdi çok daha mantıklı
Woot4Moo

@ Ancak, orada olan değerleri zaten bildiğiniz için bu gerçekten önemli değil. Temelde list.contains () enum eşdeğeri MyEnum.MyAwesomeValue
Woot4Moo

0

Guava ile daha da basit:

boolean isPartOfMyEnum(String myString){

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

}

As kioria söyledi bu işe yaramaz. MyEnum.values()MyEnum örneklerinin dizisini MyEnum.value().toString()döndürür ve bu dizi nesnesinin dize temsilini döndürür (sadece "[LMyEnum; @ 15b94ed3" gibi dize)
user2137020

Bunun .name()yerine çağırmanız gerekir .toString()(varsayılan toString yöntemini geçersiz kılmadıysanız). Daha fazla bilgi için buna bakın: enum .name () ve .toString ()
Gaʀʀʏ

0

Bu, önceki yöntemlerin tüm yaklaşımlarını birleştirir ve eşdeğer performansa sahip olmalıdır. Herhangi bir numaralandırma için kullanılabilir, @Richard H'den "Düzenle" çözümünü satır içine alır ve @bestsss gibi geçersiz değerler için İstisnalar kullanır. Tek değiş tokuş, sınıfın belirtilmesi gerektiğidir, ancak bu iki astarlıdır.

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

değerin mevcut olup olmadığını kontrol etmek için bir çözüm ve karşılığında enum değeri elde edin:

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

0

Bu benim için çalışıyor:

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

3
YourEnum "valueToCheckBlaBla" içeriyor olsa bile sürümünüz true değerini döndürür, çünkü tüm listenin dize gösteriminde "valueToCheck" bulunur.
Nicko

0

Java 8 veya üstünü kullanıyorsanız, bunu yapabilirsiniz:

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

0
  Set.of(CustomType.values())
     .contains(customTypevalue) 

0

İçerme yöntemi olarak yapabilirsiniz:

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

ya da kod bloğunuzla kullanabilirsiniz:

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

0

ayrıca şunları kullanabilirsiniz: com.google.common.base.Enums

Enums.getIfPresent (varEnum.class, varToLookFor) bir İsteğe bağlı döndürür

Enums.getIfPresent (fooEnum.class, myVariable) .isPresent ()? Enums.getIfPresent (fooEnum.class, değişkenim) .get: fooEnum.OTHERS


0

Sadece yazardım,

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

Enum # eşitliği yalnızca nesne karşılaştırma ile çalışır.


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

bu işe yaramaz. bir dize olup olmadığını kontrol ederken set enum nesneleri var.
iTake

Şimdi soru String ile ilgili olduğu gibi soruya uymuyor :)
iTake

-11

enumJava'da oldukça güçlüdür. Numaralandırmanıza kolayca bir containsyöntem ekleyebilirsiniz (bir sınıfa yöntem eklediğiniz gibi):

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

};

Bunu mu demek istediniz s.equals("b1") || s.equals("b2")?
Jigar Joshi

3
Muhtemelen bunu yapmanın en iyi yolu olmayacaktır çünkü s.equals("xx")daha sonra ekleyeceğiniz her numaralandırma için yeni eklemek zorunda kalacaksınız .
jluzwick

1
Ve 1000'den fazla numaralandırma olacak.
Jared

20
Bunun gibi korkunç çözümler öneren insanlar nasıl 64K itibar kazanır? Bu katkıda bulunanların cevaplarına dayanarak orada
atılan
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.