Java: Periksa apakah enum berisi string yang diberikan?


169

Inilah masalah saya - saya sedang mencari (jika ada) yang setara dengan enum ArrayList.contains();.

Berikut ini contoh masalah kode saya:

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

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

Sekarang, saya menyadari bahwa salah ArrayListsatu Stringsakan menjadi rute yang lebih baik di sini, tetapi saya harus menjalankan konten enum saya melalui saklar / case di tempat lain. Karena itu masalah saya.

Dengan asumsi sesuatu seperti ini tidak ada, bagaimana saya bisa melakukannya?


Switch / case dengan string diimplementasikan mulai dari Java 7
AndreyP

Jawaban:


206

Ini harus dilakukan:

public static boolean contains(String test) {

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

    return false;
}

Dengan cara ini berarti Anda tidak perlu khawatir tentang menambahkan nilai enum tambahan nanti, semuanya diperiksa.

Sunting: Jika enum sangat besar Anda bisa menempelkan nilai dalam HashSet:

public static HashSet<String> getEnums() {

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

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

  return values;
}

Maka Anda bisa melakukan: values.contains("your string")yang mengembalikan benar atau salah.


12
itu imp yang sangat buruk .: Pilihan.valueOf (tes) adalah apa yang Anda inginkan (d / coba / tangkap)
bestsss

17
bestsss, ini jelas solusi yang paling tepat. Melempar pengecualian untuk mengimplementasikan metode tipe exist () adalah praktik yang buruk. Meskipun Anda mungkin berpikir bahwa implementasi Anda lebih efisien karena tidak terlihat O (n), itu ada dalam kerangka dasar yang tidak terlihat. Juga menggunakan try {} catch menambah overhead. Plus, itu tidak cantik.
jluzwick

25
@ Jared, pasti bernilai. Tangkap saja pengecualian Anda dan kembalikan salah. Bagi mereka yang mengatakan sebaliknya, jika Anda melihat implementasinya, ia sudah menggunakan Peta, dan para pengembang JDK memiliki peluang yang jauh lebih baik untuk mengoptimalkan ini. API melempar pengecualian, yang merupakan praktik yang dapat diperdebatkan (alih-alih mengembalikan nol), tetapi ketika Anda berurusan dengan API yang melempar pengecualian, ikuti saja, jangan menciptakan kembali roda.
Yishai

2
@jluzwick try / catch overhead adalah instruksi lompatan sederhana ketika tidak diambil, tidak menggunakan Exception di catch block juga dioptimalkan. Takut mencoba / menangkap penyebab hilangnya kinerja adalah praktik yang buruk.
bestsss

22
berisi () lebih disukai daripada valueOf () dengan pengecualian. Mengapa? Karena "pengecualian, seperti namanya, akan digunakan hanya untuk kondisi luar biasa; mereka tidak boleh digunakan untuk aliran kontrol biasa" (Joshua Bloch, "Java Efektif").
james.garriss

226

Gunakan Apache commons lang3 lib sebagai gantinya

 EnumUtils.isValidEnum(MyEnum.class, myValue)

33
Catatan untuk mereka yang tertarik, implementasi mendasar yang mereka gunakan hanyalah solusi try / catch (@since 3.0 @version $ Id: EnumUtils.java 1199894 2011-11-09 17: 53: 59Z ggregory $).
Jonathan Gawrych

1
Membuat kode saya lebih sederhana, jadi saya tidak peduli apakah itu menggunakan pengecualian untuk kontrol aliran (mereka benar-benar tidak boleh) ... Akan lebih baik jika mereka mengubahnya.
jpangamarca


1
Apakah jambu biji juga mengandung solusi seperti ini?
Cypress Frankenfeld

50

Kamu bisa memakai 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
  }
}

Jika Anda mengharapkan pemeriksaan gagal sering, Anda mungkin lebih baik menggunakan loop sederhana seperti yang telah ditunjukkan - jika enum Anda mengandung banyak nilai, mungkin builda HashSetatau nilai enum serupa yang dikonversi ke string dan kueri itu HashSetsebagai gantinya.


8
Saya tidak berpikir Pengecualian adalah pilihan terbaik dalam kasus seperti itu.
GokcenG

6
Coba dan Tangkap harus menjadi pilihan terakhir. Coba dan Tangkap mahal
Jesus Dimrix

2
mengandalkan pengecualian runtime untuk melakukan logika bisnis, selain mahal, tidak dapat dibaca. mengenai pengecualian yang dicentang, ini berbeda, karena itu merupakan bagian dari bisnis.
Luís Soares

2
Ini juga mencegah siapa pun mengaktifkan jeda pada pengecualian yang dilemparkan untuk menemukan kasus luar biasa aktual yang sedang dicoba. (atau setidaknya membuatnya sangat menjengkelkan untuk melakukannya). Gunakan pengecualian untuk kasus luar biasa.
Nickolay Kondratyev

4
EnumUtils.isValidEnum(MyEnum.class, myValue)menggunakan logika yang serupa dan IMO, masuk akal untuk menambahkan seluruh perpustakaan untuk tugas sepele ini
Vikramjit Roy

37

Jika Anda menggunakan Java 1.8, Anda dapat memilih Stream + Lambda untuk mengimplementasikan ini:

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

Bahkan lebih baik:

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

};

Terima kasih untuk solusi optimis
Parth Patel

19

Enak jambu biji bisa menjadi teman Anda

Seperti misalnya ini:

enum MyData {
    ONE,
    TWO
}

@Test
public void test() {

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

11

Anda dapat terlebih dahulu mengonversi enum ke Daftar dan kemudian menggunakan metode daftar berisi

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
}

Ini harus menjadi jawaban yang diterima. Konversi ke daftar adalah cara terbersih untuk melakukannya. Itu menyelamatkan seluruh diskusi (sisi) tentang "penggunaan pengecualian yang tepat" di Jawa dalam jawaban lain di sini.
Manuel

Lempar IllegalArgumentException jika nilainya tidak ada.
mkyong

10

Beberapa asumsi:
1) Tidak mencoba / menangkap, karena itu adalah kontrol aliran yang luar biasa
2) Metode 'berisi' harus cepat, karena biasanya berjalan beberapa kali.
3) Ruang tidak terbatas (umum untuk solusi biasa)

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

Beberapa perpustakaan telah disebutkan di sini, tetapi saya merindukan perpustakaan yang sebenarnya saya cari: Spring!

Ada ObjectUtils # containConstant yang tidak sensitif huruf secara default, tetapi bisa ketat jika Anda mau. Digunakan seperti ini:

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

Catatan: Saya menggunakan metode kelebihan beban di sini untuk menunjukkan cara menggunakan pemeriksaan case sensitif. Anda dapat menghilangkan boolean untuk memiliki perilaku case-sensitive.

Berhati-hatilah dengan enum besar, karena mereka tidak menggunakan implementasi Peta seperti yang dilakukan beberapa ...

Sebagai bonus, ia juga menyediakan varian case-sensitive dari valueOf: ObjectUtils # caseInsensitiveValueOf


7

Anda bisa menggunakan ini

YourEnum {A1, A2, B1, B2}

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

Pembaruan disarankan oleh @ wightwulf1944 dimasukkan untuk membuat solusi lebih efisien.


4
Implementasi ini tidak efisien. Ini berulang melalui nilai enum untuk membuat set baru, kemudian membuat aliran, yang iterate melalui set yang dihasilkan. Ini berarti bahwa Set dan Stream baru dibuat setiap kali fungsi dipanggil, dan menggunakan stream()pada set berarti Anda mengulangi pada setiap elemen dalam set daripada mengambil keuntungan dari hashtable yang mendasarinya yang akan lebih cepat. Untuk meningkatkan ini, yang terbaik adalah cache Set yang dibuat dan gunakan contains()metode itu. Jika Anda harus mendapatkan streaming, gunakan Arrays.stream()saja.
Subaru Tashiro

3

Saya tidak berpikir ada, tetapi Anda dapat melakukan sesuatu seperti ini:

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

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

Edit:

Silakan lihat versi Richard tentang ini karena lebih tepat karena ini tidak akan berfungsi kecuali jika Anda mengonversinya untuk menggunakan Strings, yang dilakukan Richards.


tapi saya pikir OP ingin menguji string?
Richard H

ya, haha. Metode ini tidak akan efektif karena kita sudah tahu pilihan ada di enum. Modifikasi Anda lebih benar.
jluzwick

1
Jika Anda ingin mengerjakan beberapa bagian dari enum sendiri (dan bukan nama mereka), lebih baik untuk melihat EnumSet. download.oracle.com/javase/6/docs/api/java/util/EnumSet.html
Yishai

Mungkin pertanyaan bodoh, tetapi mengapa tidak .values()didokumentasikan di unduh.oracle.com/javase/6/docs/api/java/lang/Enum.html ?
Anonim

Itu pertanyaan yang bagus. Anda benar itu tidak ada dalam dokumentasi dan juga tidak ada di sumber Enum. Saya berasumsi itu hadir di salah satu implementasi Enum atau ada beberapa aturan JLS yang memungkinkan untuk itu. Juga semua objek Koleksi memiliki ini, dan ini dapat dilihat sebagai Koleksi meskipun belum tentu mengimplementasikan Koleksi.
jluzwick

3

Java Streaming menyediakan cara yang elegan untuk melakukan itu

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

Pengembalian: true jika ada elemen aliran yang cocok dengan nilai yang diberikan, jika tidak salah


2

Mengapa tidak menggabungkan balasan Pablo dengan 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;
        }
}

Tolong jangan. Lihat yang lain, jawaban yang lebih lama yang setara dengan milik Anda: stackoverflow.com/a/4936872/103412
Torsten

1

Pendekatan ini dapat digunakan untuk memeriksa Enum, Anda dapat menambahkannya ke Utilskelas:

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

Gunakan seperti ini:

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

1

Saya membuat kelas berikutnya untuk validasi ini

public class EnumUtils {

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

}

contoh penggunaan:

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

Anda dapat menggunakan valueOf("a1")jika Anda ingin mencari dengan menggunakan String


3
tapi itu tidak elegan .. jika nilainya tidak ada itu memunculkan eksepsi. jadi Anda mungkin harus mengelilinginya dengan mencoba menangkap
Kasturi

Itu akan membuang pengecualian jika nilainya tidak ada
Richard H

Kurang anggun dari iterasi melalui pilihan enum mencari objek yang cocok?
jprete

0

Ini adalah enum, itu adalah nilai konstan jadi jika dalam pernyataan switch hanya melakukan sesuatu seperti ini:

case: val1
case: val2

Juga mengapa Anda perlu tahu apa yang dinyatakan sebagai konstanta?


Ini sedikit kode tidak dalam pernyataan switch berkata, itu di tempat lain. Saya hanya menyatakan bahwa dalam kasus ini, enum diperlukan karena yang lain menyarankan agar saya menggunakan ArrayList sebagai gantinya.
Jared

@ Jared yang jauh lebih masuk akal sekarang
Woot4Moo

@ Jared Namun itu tidak terlalu penting karena Anda sudah tahu nilai-nilai yang ada di sana. Pada dasarnya setara enum dari list.contains () adalah MyEnum.MyAwesomeValue
Woot4Moo

0

Dengan jambu bahkan lebih sederhana:

boolean isPartOfMyEnum(String myString){

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

}

Seperti yang dikatakan kioria , ini tidak akan berhasil. MyEnum.values()mengembalikan array instance MyEnum dan MyEnum.value().toString()mengembalikan representasi string dari objek array ini (hanya string seperti "[LMyEnum; @ 15b94ed3")
user2137020

Anda harus memanggil .name()alih-alih .toString()(kecuali Anda mengganti metode toString default). Lihat ini untuk info lebih lanjut: Perbedaan antara enum .name () dan .toString ()
Ga

0

Ini menggabungkan semua pendekatan dari metode sebelumnya dan harus memiliki kinerja yang setara. Ini dapat digunakan untuk enum apa pun, menguraikan solusi "Edit" dari @Richard H, dan menggunakan Pengecualian untuk nilai yang tidak valid seperti @bestsss. Satu-satunya tradeoff adalah bahwa kelas perlu ditentukan, tetapi yang mengubah ini menjadi dua baris.

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

solusi untuk memeriksa apakah nilai hadir dan dapatkan nilai enum sebagai imbalan:

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

0

Yang ini bekerja untuk saya:

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

3
Versi Anda akan kembali benar bahkan jika YourEnum berisi "valueToCheckBlaBla", karena "valueToCheck" akan hadir dalam representasi string dari seluruh daftar.
Nicko

0

Jika Anda Menggunakan Java 8 atau lebih baru, Anda bisa melakukan ini:

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

Anda dapat menjadikannya sebagai metode berisi:

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

atau Anda bisa menggunakannya dengan blok kode Anda:

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

0

Anda juga dapat menggunakan: com.google.common.base.Enums

Enums.getIfPresent (varEnum.class, varToLookFor) mengembalikan Opsional

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


0

Saya hanya akan menulis,

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

Enum # sama dengan hanya berfungsi dengan membandingkan objek.


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

itu tidak akan berhasil. set memiliki objek enum, sementara Anda sedang memeriksa string.
SAAT

sekarang jawabannya tidak cocok dengan pertanyaan, seperti tentang String :)
iTake

-11

enumcukup kuat di Jawa. Anda dapat dengan mudah menambahkan containsmetode ke enum Anda (seperti Anda akan menambahkan metode ke kelas):

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

};

maksud anda s.equals("b1") || s.equals("b2")??
Jigar Joshi

3
Ini mungkin bukan cara terbaik untuk melakukannya karena Anda harus menambahkan yang baru s.equals("xx")untuk setiap enum yang Anda tambahkan nanti.
jluzwick

1
Dan akan ada 1000+ enum.
Jared

20
Bagaimana orang yang menyarankan solusi mengerikan seperti ini, mendapat reputasi 64 ribu? Aku benci memikirkan semua kode jelek yang disandang di luar sana berdasarkan jawaban kontributor ini
Dexygen
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.