Java: ตรวจสอบว่า enum มีสตริงที่กำหนดหรือไม่


169

นี่เป็นปัญหาของฉัน - ฉันกำลังมองหา (ถ้ามันยังมีอยู่) เทียบเท่า enum ArrayList.contains();ของ

นี่คือตัวอย่างของปัญหารหัสของฉัน:

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

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

ตอนนี้ฉันรู้ว่าหนึ่งArrayListในStringsนั้นจะเป็นเส้นทางที่ดีกว่าที่นี่ แต่ฉันต้องเรียกใช้เนื้อหา enum ของฉันผ่านสวิตช์ / เคสที่อื่น ดังนั้นปัญหาของฉัน

สมมติว่าไม่มีอะไรแบบนี้ฉันจะทำยังไงต่อ?


Switch / case with strings ถูกนำมาใช้ตั้งแต่ Java 7
AndreyP

คำตอบ:


205

สิ่งนี้ควรทำ:

public static boolean contains(String test) {

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

    return false;
}

วิธีนี้หมายความว่าคุณไม่ต้องกังวลเกี่ยวกับการเพิ่มค่า enum เพิ่มเติมในภายหลังพวกเขาทั้งหมดจะถูกตรวจสอบ

แก้ไข:ถ้า enum มีขนาดใหญ่มากคุณสามารถใส่ค่าใน HashSet ได้:

public static HashSet<String> getEnums() {

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

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

  return values;
}

จากนั้นคุณสามารถทำได้values.contains("your string")ซึ่งผลตอบแทนจริงหรือเท็จ


12
นั่นเป็นความคิดที่แย่มาก: Choice.valueOf (การทดสอบ) คือสิ่งที่คุณต้องการ (ด้วยการลอง / จับ)
bestsss

17
bestsss นี่เป็นทางออกที่เหมาะสมที่สุดอย่างชัดเจน การขว้างข้อยกเว้นเพื่อใช้วิธีการที่มีอยู่ () เป็นวิธีปฏิบัติที่เลว แม้ว่าคุณอาจคิดว่าการใช้งานของคุณมีประสิทธิภาพมากขึ้นเนื่องจากไม่ได้มอง O (n) แต่อยู่ในกรอบพื้นฐานที่มองไม่เห็น นอกจากนี้การใช้ try {} catch ช่วยเพิ่มค่าใช้จ่าย นอกจากนี้มันก็ไม่ได้สวย
jluzwick

25
@ Jared คุ้มค่าแน่นอน เพียงแค่จับข้อยกเว้นของคุณและกลับเท็จ สำหรับผู้ที่พูดเป็นอย่างอื่นหากคุณดูที่การนำไปใช้งานจะใช้แผนที่อยู่แล้วและนักพัฒนา JDK มีโอกาสที่ดีกว่ามากในการปรับให้เหมาะสม API โยนข้อยกเว้นซึ่งเป็นวิธีปฏิบัติที่ถกเถียงกัน (แทนการส่งคืนค่า Null) แต่เมื่อคุณจัดการกับ API ที่ส่งข้อยกเว้นให้ไปด้วยอย่าใช้นวัตกรรมใหม่
Yishai

2
@jluzwick ลอง / จับค่าโสหุ้ยเป็นคำสั่งการกระโดดแบบง่าย ๆ เมื่อไม่ได้ถ่าย, ไม่ได้ใช้การยกเว้นในการบล็อก catch ยังได้รับการปรับให้เหมาะสม สาเหตุที่น่ากลัวลอง / จับสาเหตุของการสูญเสียประสิทธิภาพเป็นการปฏิบัติที่ไม่ดี
bestsss

22
contain () ต้องการใช้มากกว่า valueOf () พร้อมข้อยกเว้น ทำไม? เนื่องจาก "ข้อยกเว้นเป็นชื่อของพวกเขาที่มีความหมายว่าจะใช้สำหรับเงื่อนไขพิเศษเท่านั้นจึงไม่ควรใช้สำหรับการควบคุมทั่วไป" (Joshua Bloch, "Effective Java")
james.garriss

226

ใช้ Apache Commons lang3 lib แทน

 EnumUtils.isValidEnum(MyEnum.class, myValue)

33
หมายเหตุสำหรับผู้ที่สนใจการใช้งานพื้นฐานที่พวกเขาใช้นั้นเป็นเพียงโซลูชันลอง / จับ (@since 3.0 @version $ Id: EnumUtils.java 1199894 2011-11-09 17: 53: 59Z ggregory $)
Jonathan Gawrych

1
ทำให้รหัสของฉันง่ายขึ้นดังนั้นฉันจึงไม่แคร์ว่าจะใช้ข้อยกเว้นสำหรับการควบคุมการไหล (พวกเขาไม่ควรจริง ๆ ) ... มันจะดีถ้าพวกเขาเปลี่ยนมัน
jpangamarca


1
ฝรั่งมีวิธีแก้ปัญหาเช่นนี้หรือไม่?
Cypress Frankenfeld

50

คุณสามารถใช้ได้ 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
  }
}

หากคุณคิดว่าการตรวจสอบที่จะล้มเหลวบ่อยครั้งคุณอาจจะดีกว่าการใช้ห่วงง่ายๆกับคนอื่น ๆ ได้แสดงให้เห็น - ถ้า enums ของคุณมีหลายค่าบางที builda HashSetหรือคล้ายกันของค่า enum ของคุณแปลงเป็นสตริงและแบบสอบถามที่HashSetแทน


8
ฉันไม่คิดว่าการยกเว้นเป็นตัวเลือกที่ดีที่สุดในกรณีเช่นนี้
GokcenG

6
ลองและจับควรจะเป็นทางเลือกสุดท้าย ลองและจับมีราคาแพง
พระเยซู Dimrix

2
การใช้ข้อยกเว้นรันไทม์เพื่อทำตรรกะทางธุรกิจนอกเหนือจากราคาแพงนั้นไม่สามารถอ่านได้ เกี่ยวกับข้อยกเว้นที่ตรวจสอบแตกต่างกันเนื่องจากสิ่งเหล่านั้นเป็นส่วนหนึ่งของธุรกิจ
Luís Soares

2
นอกจากนี้ยังป้องกันไม่ให้ใครก็ตามเปิดใช้ข้อยกเว้นแบบกว้างในการโยนข้อยกเว้นเพื่อค้นหากรณีพิเศษที่เกิดขึ้นจริงที่กำลังลองใหม่ (หรืออย่างน้อยก็ทำให้มันน่ารำคาญมาก) ใช้ข้อยกเว้นสำหรับกรณีพิเศษ
Nickolay Kondratyev

4
EnumUtils.isValidEnum(MyEnum.class, myValue)ใช้ตรรกะที่คล้ายกันและ IMO มันทำให้รู้สึกถึงการเพิ่มห้องสมุดทั้งหมดสำหรับงานเล็ก ๆ น้อย ๆ นี้
Vikramjit Roy

37

หากคุณใช้ Java 1.8 คุณสามารถเลือก Stream + Lambda เพื่อใช้สิ่งนี้:

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

ดียิ่งขึ้น:

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

};

ขอบคุณสำหรับการแก้ปัญหาในแง่ดี
Parth Patel

19

Guavas Enumsอาจเป็นเพื่อนของคุณ

เช่นนี้

enum MyData {
    ONE,
    TWO
}

@Test
public void test() {

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

11

คุณสามารถแปลง enum เป็น List ก่อนจากนั้นใช้ list มีวิธีการ

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
}

นี่ควรเป็นคำตอบที่ยอมรับได้ การแปลงเป็นลิสต์เป็นวิธีที่สะอาดที่สุด มันอะไหล่การอภิปราย (ด้าน) ทั้งหมดเกี่ยวกับ "การใช้งานที่เหมาะสมของข้อยกเว้น" ใน Java ในคำตอบอื่น ๆ ที่นี่
Manuel

โยน IllegalArgumentException หากไม่มีค่าอยู่
mkyong

10

มีการพูดถึงห้องสมุดคู่กันที่นี่ แต่ฉันคิดถึงสิ่งที่ฉันกำลังมองหา: ฤดูใบไม้ผลิ!

มีObjectUtils # containConstantซึ่งไม่คำนึงถึงขนาดตัวพิมพ์โดยค่าเริ่มต้น แต่สามารถเข้มงวดได้ถ้าคุณต้องการ มันถูกใช้อย่างนี้:

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

หมายเหตุ: ฉันใช้วิธีโอเวอร์โหลดที่นี่เพื่อสาธิตวิธีใช้การตรวจสอบด้วยตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ คุณสามารถละเว้นบูลีนเพื่อให้มีพฤติกรรมที่ไม่สนใจตัวพิมพ์เล็กและใหญ่

ระวังด้วย enums ขนาดใหญ่เนื่องจากพวกเขาไม่ได้ใช้การใช้งานแผนที่เนื่องจากบางคนทำ ...

เป็นโบนัสนอกจากนี้ยังมีชุดตัวพิมพ์เล็กและตัวพิมพ์เล็กของ valueOf: ObjectUtils # caseInsensitiveValueOf


9

สมมติฐานบางประการ:
1) ไม่ต้องลอง / จับเนื่องจากมีการควบคุมการไหลที่ยอดเยี่ยม
2) เมธอด 'contain' จะต้องรวดเร็วเนื่องจากมักจะทำงานหลายครั้ง
3) พื้นที่ไม่ จำกัด (โดยทั่วไปสำหรับวิธีแก้ไขปัญหาทั่วไป)

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

คุณสามารถใช้สิ่งนี้

YourEnum {A1, A2, B1, B2}

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

อัปเดตที่แนะนำโดย @ wightwulf1944 ได้ถูกรวมไว้เพื่อให้การแก้ปัญหามีประสิทธิภาพมากขึ้น


4
การใช้งานนี้ไม่มีประสิทธิภาพ สิ่งนี้วนซ้ำผ่านค่า enum เพื่อสร้างชุดใหม่จากนั้นสร้างสตรีมที่ทำซ้ำผ่านชุดผลลัพธ์ ซึ่งหมายความว่าชุดใหม่และสตรีมจะถูกสร้างขึ้นทุกครั้งที่มีการเรียกใช้ฟังก์ชั่นและการใช้งานstream()ในชุดหมายความว่าคุณกำลังวนซ้ำในแต่ละองค์ประกอบในชุดแทนที่จะใช้ประโยชน์จาก hashtable ที่สำคัญซึ่งจะเร็วขึ้น หากต้องการปรับปรุงสิ่งนี้สิ่งที่ดีที่สุดคือแคชชุดที่สร้างและใช้เป็นcontains()วิธีแทน หากคุณต้องได้รับกระแสข้อมูลให้ใช้Arrays.stream()แทน
Subaru Tashiro

3

ฉันไม่คิดว่ามี แต่คุณสามารถทำสิ่งนี้:

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

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

แก้ไข:

โปรดดูเวอร์ชันนี้ของ Richard เนื่องจากเหมาะสมกว่าเนื่องจากจะไม่ทำงานเว้นแต่คุณจะแปลงเป็น Strings ซึ่ง Richards ทำ


แต่ฉันคิดว่า OP ต้องการทดสอบสตริงหรือไม่
Richard H

ใช่ฮ่าฮ่า วิธีนี้จะไม่ได้ผลเท่าที่เรารู้อยู่แล้วว่าทางเลือกอยู่ใน enums การดัดแปลงของคุณถูกต้องมากขึ้น
jluzwick

1
หากคุณต้องการที่จะทำงานกับบางส่วนของ Enums ตัวเอง (และไม่ใช่ชื่อของพวกเขา) ดีกว่าที่จะดู EnumSet download.oracle.com/javase/6/docs/api/java/util/EnumSet.html
Yishai

บางทีอาจจะเป็นคำถามโง่ แต่ทำไมไม่ได้เป็น.values()เอกสารที่download.oracle.com/javase/6/docs/api/java/lang/Enum.html ?
Anonym

นั่นเป็นคำถามที่ดี คุณพูดถูกมันไม่มีอยู่ในเอกสารประกอบและมันไม่มีอยู่ในแหล่ง Enum เช่นกัน ฉันสมมติว่ามันมีอยู่ในการนำไปใช้อย่างใดอย่างหนึ่งของ Enum หรือมีกฎ JLS บางอย่างที่อนุญาต วัตถุคอลเลกชันทั้งหมดมีสิ่งนี้และสิ่งนี้สามารถมองได้ว่าเป็นคอลเลกชันแม้ว่าจะไม่จำเป็นต้องใช้คอลเลกชันก็ตาม
jluzwick

3

Java Streams เป็นวิธีที่ยอดเยี่ยมในการทำเช่นนั้น

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

คืนค่า: จริงถ้าองค์ประกอบใด ๆ ของกระแสตรงกับค่าที่ระบุมิฉะนั้นเป็นเท็จ



1

วิธีนี้สามารถใช้ในการตรวจสอบใด ๆEnumคุณสามารถเพิ่มเข้าไปในUtilsชั้นเรียน:

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

ใช้วิธีนี้:

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

1

ฉันสร้างคลาสถัดไปสำหรับการตรวจสอบนี้

public class EnumUtils {

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

}

ตัวอย่างการใช้งาน:

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


3
แต่นั่นไม่ใช่ความสง่างาม .. หากไม่มีค่ามันจะทำให้เกิดข้อยกเว้น ดังนั้นคุณอาจต้องล้อมรอบด้วยลองจับ
Kasturi

นั่นจะทำให้เกิดข้อยกเว้นหากไม่มีค่า
Richard H

หรูหราน้อยกว่าการวนซ้ำผ่านตัวเลือก enum ที่กำลังมองหาวัตถุที่ตรงกันหรือไม่
jprete

0

มันคือ enum, มันคือค่าคงที่ดังนั้นถ้ามันอยู่ในคำสั่ง switch มันแค่ทำอะไรแบบนี้:

case: val1
case: val2

ทำไมคุณต้องรู้ว่าอะไรคือค่าคงที่?


โค้ดนี้ไม่ได้อยู่ในคำสั่ง switch ดังกล่าวซึ่งอยู่ที่อื่น ฉันเพียงแค่ระบุว่าในกรณีนี้จำเป็นต้อง enum ตามที่คนอื่นแนะนำว่าฉันใช้ ArrayList แทน
Jared

@ Jared ที่เข้าใจได้ง่ายกว่าตอนนี้
Woot4Moo

@Jared แต่นั่นไม่สำคัญจริงๆเพราะคุณรู้ค่าที่มีอยู่แล้ว โดยทั่วไปเทียบเท่า enum ของ list.contain () คือ MyEnum.MyAwesomeValue
Woot4Moo

0

ด้วยฝรั่งมันง่ายกว่ามาก:

boolean isPartOfMyEnum(String myString){

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

}

ในฐานะที่เป็นkioriaกล่าวว่าสิ่งนี้จะไม่ทำงาน MyEnum.values()ส่งคืนอาร์เรย์ของอินสแตนซ์ MyEnum และMyEnum.value().toString()ส่งกลับการแทนค่าสตริงของวัตถุอาร์เรย์นี้ (เพียงแค่สตริงเช่น "[LMyEnum; @ 15b94ed3")
user2137020

คุณต้องโทร.name()แทน.toString()(เว้นแต่คุณจะแทนที่วิธี toString เริ่มต้น) ดูสิ่งนี้สำหรับข้อมูลเพิ่มเติม: ความแตกต่างระหว่าง enum .name () และ. toString ()
Gaʀʀʏ

0

วิธีนี้รวมวิธีการทั้งหมดจากวิธีการก่อนหน้านี้และควรมีประสิทธิภาพที่เทียบเท่า มันสามารถใช้สำหรับ enum ใด ๆ แทรกแนวทาง "แก้ไข" จาก @Richard H และใช้ข้อยกเว้นสำหรับค่าที่ไม่ถูกต้องเช่น @bestsss ข้อเสียเพียงอย่างเดียวคือต้องระบุคลาส แต่เปลี่ยนเป็นสองซับ

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

วิธีการแก้ปัญหาเพื่อตรวจสอบว่ามีมูลค่าหรือไม่และรับค่า enum เป็นการตอบแทน:

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

0

อันนี้ใช้ได้สำหรับฉัน:

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

3
เวอร์ชันของคุณจะกลับมาจริงแม้ว่า YourEnum จะมี "valueToCheckBlaBla" เพราะ "valueToCheck" จะปรากฏในการแสดงสตริงของรายการทั้งหมด
Nicko

0

หากคุณใช้ Java 8 ขึ้นไปคุณสามารถทำสิ่งนี้ได้:

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


0

คุณสามารถทำให้มันเป็นวิธีการบรรจุ:

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

หรือคุณสามารถใช้มันกับโค้ดบล็อกของคุณ:

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

0

คุณสามารถใช้: com.google.common.base.Enums

Enums.getIfPresent (varEnum.class, varToLookFor) จะส่งคืนตัวเลือก

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


0

ฉันแค่จะเขียน

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

Enum # เท่ากับใช้งานได้กับการเปรียบเทียบวัตถุเท่านั้น


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

ที่ไม่ทำงาน ชุดมีวัตถุ enum ในขณะที่คุณกำลังตรวจสอบมันสำหรับสตริง
iTake

ตอนนี้คำตอบที่ไม่ตรงกับคำถามที่มันเป็นเรื่องเกี่ยวกับสตริง :)
iTake

-11

enumค่อนข้างทรงพลังใน Java คุณสามารถเพิ่มcontainsวิธีการใน enum ของคุณได้อย่างง่ายดาย(เช่นคุณจะเพิ่มวิธีการในชั้นเรียน):

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

};

คุณไม่ได้หมายความว่า s.equals("b1") || s.equals("b2")??
Jigar Joshi

3
นี่อาจจะไม่ใช่วิธีที่ดีที่สุดในการทำเพราะคุณจะต้องเพิ่มใหม่s.equals("xx")สำหรับแต่ละ enum ที่คุณเพิ่มในภายหลัง
jluzwick

1
และจะมีค่ามากกว่า 1,000 ค่า
Jared

20
คนที่แนะนำวิธีแก้ปัญหาที่น่ากลัวเช่นนี้ได้รับชื่อเสียง 64K อย่างไร ฉันเกลียดที่จะคิดว่าทุกรหัสเป็นเส็งเคร็งสะพายออกมีขึ้นอยู่กับผู้ร่วมสมทบนี้คำตอบ
Dexygen
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.