2009-07-22 4 views
45

क्या यह जांचने के लिए वैसे भी है कि कोई enum किसी दिए गए स्ट्रिंग से तुलना करके मौजूद है या नहीं? मुझे ऐसा कोई काम नहीं मिल रहा है। मैं बस valueOf विधि का उपयोग करने की कोशिश कर सकता हूं और अपवाद पकड़ सकता हूं लेकिन मुझे सिखाया गया है कि रनटाइम अपवादों को पकड़ना अच्छा अभ्यास नहीं है। किसी के पास कोई विचार है?जांचें कि जावा में enum मौजूद है

+6

मैं वास्तव में emum value के पीछे विचार को समझ नहीं पा रहा हूं अपवाद फेंकने से ... यह कोई समझ नहीं आता है। यह हर पहलू में बहुत अधिक व्यावहारिक होगा यदि यह सिर्फ शून्य लौटाएगा। – marcolopes

उत्तर

41

मुझे नहीं लगता कि अपवादों को पकड़ने के बिना ऐसा करने का एक अंतर्निहित तरीका है।

public static MyEnum asMyEnum(String str) { 
    for (MyEnum me : MyEnum.values()) { 
     if (me.name().equalsIgnoreCase(str)) 
      return me; 
    } 
    return null; 
} 

संपादित करें:: आप के बजाय कुछ इस तरह इस्तेमाल कर सकते हैं जॉन स्कीट नोट, एक निजी समर्थन सरणी हर बार यह कहा जाता है क्लोनिंग द्वारा values() काम करता है के रूप में। यदि प्रदर्शन महत्वपूर्ण है, तो आप केवल एक बार values() पर कॉल कर सकते हैं, सरणी को कैश कर सकते हैं, और इसके माध्यम से पुनरावृत्त कर सकते हैं।

इसके अलावा, यदि आपके enum की बड़ी संख्या में मूल्य हैं, तो जॉन स्कीट का नक्शा विकल्प किसी सरणी पुनरावृत्ति से बेहतर प्रदर्शन करने की संभावना है।

4

मुझे नहीं पता कि किसी ने आपको क्यों बताया कि रनटाइम अपवादों को पकड़ना बुरा था।

valueOf का उपयोग करें और IllegalArgumentException को पकड़ना एक स्ट्रिंग को एक enum में बदलने/जांचने के लिए ठीक है।

+19

नहीं, यह नहीं है, आईएमओ। यह अपवादों के माध्यम से एक असाधारण स्थिति का परीक्षण कर रहा है - सामान्य, गैर-त्रुटि स्थिति में प्रवाह नियंत्रण के लिए उनका उपयोग करना। यह अपवाद आईएमओ का बहुत खराब उपयोग है, और एक ऐसा जो महत्वपूर्ण प्रदर्शन प्रभाव डाल सकता है। सामान्य रूप से प्रदर्शन के मामले में अपवाद ठीक होते हैं, क्योंकि उन्हें नहीं होना चाहिए - लेकिन जब आप उन्हें गैर-त्रुटि स्थितियों के लिए उपयोग करते हैं, तो कोड जो * दिखता है * जैसे इसे जल्दी से चलाना चाहिए, उसे नीचे दबाया जा सकता है। –

+6

मजेदार बात - इस राय के आधार पर (जिसे मैं आम तौर पर भी साझा करूंगा), मैंने अपाचे कॉमन्स 'EnumUtils.isValidEnum 'का उपयोग करने का फैसला किया ताकि इसे" साफ "संभव बनाया जा सके। और अनुमान लगाएं कि कैसे 'EnumUtils.isValidEnum' लागू किया गया है - पाठ्यक्रम का' अवैध अर्ग्यूमेंट अपवाद 'पकड़ना :-) –

+0

सबसे छोटा कामकाजी समाधान। –

53

यदि मुझे ऐसा करने की ज़रूरत है, तो मैं कभी-कभी नामों के Set<String> या यहां तक ​​कि अपने स्वयं के Map<String,MyEnum> का निर्माण करता हूं - तो आप बस इसे देख सकते हैं।

ध्यान देने योग्य अंक की एक जोड़ी:

  • एक स्थिर प्रारंभकर्ता में किसी भी तरह के स्थिर संग्रह आबाद। एक वैरिएबल प्रारंभकर्ता का उपयोग न करें और फिर उस पर भरोसा करें जब एनम कन्स्ट्रक्टर चलता है - यह नहीं होता! (स्थिर प्रारंभकर्ता से पहले, enum रचनाकार निष्पादित करने वाली पहली चीजें हैं।)
  • values() का उपयोग करने से बचने के लिए प्रयास करें - इसे हर बार एक नई सरणी बनाना और पॉप्युलेट करना है। सभी तत्वों को पुन: सक्रिय करने के लिए, EnumSet.allOf का उपयोग करें जो बड़ी संख्या में तत्वों के बिना enums के लिए अधिक कुशल है।

नमूना कोड:

import java.util.*; 

enum SampleEnum { 
    Foo, 
    Bar; 

    private static final Map<String, SampleEnum> nameToValueMap = 
     new HashMap<String, SampleEnum>(); 

    static { 
     for (SampleEnum value : EnumSet.allOf(SampleEnum.class)) { 
      nameToValueMap.put(value.name(), value); 
     } 
    } 

    public static SampleEnum forName(String name) { 
     return nameToValueMap.get(name); 
    } 
} 

public class Test { 
    public static void main(String [] args) 
     throws Exception { // Just for simplicity! 
     System.out.println(SampleEnum.forName("Foo")); 
     System.out.println(SampleEnum.forName("Bar")); 
     System.out.println(SampleEnum.forName("Baz")); 
    } 
} 
बेशक

, यदि आप केवल कुछ ही नाम है यह शायद overkill है - एक हे (एन) समाधान अक्सर (1) समाधान एक ओ से अधिक जीतता है जब n छोटा है पर्याप्त।

import java.util.*; 

enum SampleEnum { 
    Foo, 
    Bar; 

    // We know we'll never mutate this, so we can keep 
    // a local copy. 
    private static final SampleEnum[] copyOfValues = values(); 

    public static SampleEnum forName(String name) { 
     for (SampleEnum value : copyOfValues) { 
      if (value.name().equals(name)) { 
       return value; 
      } 
     } 
     return null; 
    } 
} 

public class Test { 
    public static void main(String [] args) 
     throws Exception { // Just for simplicity! 
     System.out.println(SampleEnum.forName("Foo")); 
     System.out.println(SampleEnum.forName("Bar")); 
     System.out.println(SampleEnum.forName("Baz")); 
    } 
} 
+0

'मान() 'एक नई सरणी बनाता है और पॉप्युलेट करता है? मुझे पहले सुनवाई याद नहीं है, लेकिन संभवतः आपके पास एक स्रोत है? –

+7

वैसे यह एक सरणी लौटाता है ... और यह आपको उस सरणी को म्यूट करने से नहीं बचा सकता ... और इसके बाद कॉलर्स को इससे बाधा नहीं आती। इसके अलावा, जेआरई स्रोतों को देखें :) –

+0

यह जेआरई स्रोतों में नहीं है, इसलिए मैंने पूछा। लेकिन मैंने उत्परिवर्तन पहलू के बारे में सोचा नहीं था; आप शायद सही हो, तो। –

4

जॉन स्कीट जवाब के आधार पर मैं एक वर्ग काम पर आसानी से यह करने के लिए अनुमति देता है बनाया है:

import com.google.common.collect.ImmutableMap; 
import com.google.common.collect.Maps; 

import java.util.EnumSet; 
import java.util.HashSet; 
import java.util.Map; 
import java.util.Set; 

/** 
* <p> 
* This permits to easily implement a failsafe implementation of the enums's valueOf 
* Better use it inside the enum so that only one of this object instance exist for each enum... 
* (a cache could solve this if needed) 
* </p> 
* 
* <p> 
* Basic usage exemple on an enum class called MyEnum: 
* 
* private static final FailSafeValueOf<MyEnum> FAIL_SAFE = FailSafeValueOf.create(MyEnum.class); 
* public static MyEnum failSafeValueOf(String enumName) { 
*  return FAIL_SAFE.valueOf(enumName); 
* } 
* 
* </p> 
* 
* <p> 
* You can also use it outside of the enum this way: 
* FailSafeValueOf.create(MyEnum.class).valueOf("EnumName"); 
* </p> 
* 
* @author Sebastien Lorber <i>([email protected])</i> 
*/ 
public class FailSafeValueOf<T extends Enum<T>> { 

    private final Map<String,T> nameToEnumMap; 

    private FailSafeValueOf(Class<T> enumClass) { 
     Map<String,T> map = Maps.newHashMap(); 
     for (T value : EnumSet.allOf(enumClass)) { 
      map.put(value.name() , value); 
     } 
     nameToEnumMap = ImmutableMap.copyOf(map); 
    } 

    /** 
    * Returns the value of the given enum element 
    * If the 
    * @param enumName 
    * @return 
    */ 
    public T valueOf(String enumName) { 
     return nameToEnumMap.get(enumName); 
    } 

    public static <U extends Enum<U>> FailSafeValueOf<U> create(Class<U> enumClass) { 
     return new FailSafeValueOf<U>(enumClass); 
    } 

} 

और इकाई परीक्षण:

import org.testng.annotations.Test; 

import static org.testng.Assert.*; 


/** 
* @author Sebastien Lorber <i>([email protected])</i> 
*/ 
public class FailSafeValueOfTest { 

    private enum MyEnum { 
     TOTO, 
     TATA, 
     ; 

     private static final FailSafeValueOf<MyEnum> FAIL_SAFE = FailSafeValueOf.create(MyEnum.class); 
     public static MyEnum failSafeValueOf(String enumName) { 
      return FAIL_SAFE.valueOf(enumName); 
     } 
    } 

    @Test 
    public void testInEnum() { 
     assertNotNull(MyEnum.failSafeValueOf("TOTO")); 
     assertNotNull(MyEnum.failSafeValueOf("TATA")); 
     assertNull(MyEnum.failSafeValueOf("TITI")); 
    } 

    @Test 
    public void testInApp() { 
     assertNotNull(FailSafeValueOf.create(MyEnum.class).valueOf("TOTO")); 
     assertNotNull(FailSafeValueOf.create(MyEnum.class).valueOf("TATA")); 
     assertNull(FailSafeValueOf.create(MyEnum.class).valueOf("TITI")); 
    } 

} 

यहाँ एक और तरीका है ध्यान दें कि मैंने एक अपरिवर्तनीय मैप बनाने के लिए अमरूद का उपयोग किया था, लेकिन वास्तव में आप एक सामान्य मानचित्र का उपयोग कर सकते हैं, क्योंकि मुझे लगता है कि नक्शा कभी वापस नहीं आया है ...

20

मेरी पसंदीदा lib में से एक: अपाचे कॉमन्स।

EnumUtils आसानी से ऐसा कर सकता है।

यहाँ एक Enum मान्य करने के लिए एक नमूना कोड है:

MyEnum strTypeEnum = null; 

// test if String str is compatible with the enum 
if(EnumUtils.isValidEnum(MyEnum.class, str)){ 
    strTypeEnum = MyEnum.valueOf(str); 
} 
+0

अच्छा न्यूनतम समाधान – mumair

+0

आपकी टिप्पणी के लिए धन्यवाद। असल में यह कम से कम है क्योंकि दूसरों द्वारा किए गए libs का उपयोग करते हुए, अक्सर इसे स्वयं कोड करने से सबसे अच्छा समाधान होता है। अगर आप इसे स्वयं करते हैं आप एक ही समाधान ("पहिया को पुन: पेश करने") तक पहुंच रहे हैं या आप कुछ महत्वपूर्ण याद करते हैं और यह सबसे खराब व्यवहार करता है (फिर उस स्थिति में आप "स्क्वायर व्हील को पुन: पेश करते हैं") –

+1

हां बिल्कुल। अपाचे कॉमन्स पूर्ण रसोईघर है, केवल तभी आपको इससे पूर्ण या अधिकतम लाभ मिलता है :) स्क्वायर व्हील: पी – mumair

1

तुम भी अमरूद का उपयोग करें और कुछ इस तरह कर सकते हैं: (Google Guava) पुस्तकालय

// This method returns enum for a given string if it exists, otherwise it returns default enum. 
private MyEnum getMyEnum(String enumName) { 
    // It is better to return default instance of enum instead of null 
    return hasMyEnum(enumName) ? MyEnum.valueOf(enumName) : MyEnum.DEFAULT; 
} 

// This method checks that enum for a given string exists. 
private boolean hasMyEnum(String enumName) { 
    return Iterables.any(Arrays.asList(MyEnum.values()), new Predicate<MyEnum>() { 
    public boolean apply(MyEnum myEnum) { 
     return myEnum.name().equals(enumName); 
    } 
    }); 
} 

दूसरी विधि में मैं अमरूद का उपयोग जो बहुत उपयोगी Iterables कक्षा प्रदान करता है। Iterables.any() विधि का उपयोग करके हम जांच सकते हैं कि सूची वस्तु में दिया गया मान मौजूद है या नहीं। इस विधि को दो पैरामीटर की आवश्यकता है: एक सूची और ऑब्जेक्ट को प्रमाणित करें। सबसे पहले मैंने Arrays.asList() सभी enums के साथ एक सूची बनाने के लिए विधि का उपयोग किया। उसके बाद मैंने नया बनाया ऑब्जेक्ट का प्रयोग करें जिसका उपयोग यह जांचने के लिए किया जाता है कि कोई दिया गया तत्व (हमारे मामले में enum) में स्थिति को संतुष्ट करता है विधि लागू करें। यदि ऐसा होता है, तो विधि Iterables.any() सही मान देता है।

1

अधिकांश उत्तरों या तो enum.valueOf() के साथ प्रयास/पकड़ का उपयोग करने के लिए बराबर के साथ एक लूप का उपयोग करने का सुझाव देते हैं। मैं जानना चाहता था कि कौन सी विधि तेज है और कोशिश की। मैं बेंचमार्किंग पर बहुत अच्छा नहीं हूं, इसलिए अगर मैंने कोई गलती की है तो कृपया मुझे सही करें।

यहाँ मेरा मुख्य वर्ग के कोड:

package enumtest; 

public class TestMain { 

    static long timeCatch, timeIterate; 
    static String checkFor; 
    static int corrects; 

    public static void main(String[] args) { 
     timeCatch = 0; 
     timeIterate = 0; 
     TestingEnum[] enumVals = TestingEnum.values(); 
     String[] testingStrings = new String[enumVals.length * 5]; 
     for (int j = 0; j < 10000; j++) { 
      for (int i = 0; i < testingStrings.length; i++) { 
       if (i % 5 == 0) { 
        testingStrings[i] = enumVals[i/5].toString(); 
       } else { 
        testingStrings[i] = "DOES_NOT_EXIST" + i; 
       } 
      } 

      for (String s : testingStrings) { 
       checkFor = s; 
       if (tryCatch()) { 
        ++corrects; 
       } 
       if (iterate()) { 
        ++corrects; 
       } 
      } 
     } 

     System.out.println(timeCatch/1000 + "us for try catch"); 
     System.out.println(timeIterate/1000 + "us for iterate"); 
     System.out.println(corrects); 
    } 

    static boolean tryCatch() { 
     long timeStart, timeEnd; 
     timeStart = System.nanoTime(); 
     try { 
      TestingEnum.valueOf(checkFor); 
      return true; 
     } catch (IllegalArgumentException e) { 
      return false; 
     } finally { 
      timeEnd = System.nanoTime(); 
      timeCatch += timeEnd - timeStart; 
     } 

    } 

    static boolean iterate() { 
     long timeStart, timeEnd; 
     timeStart = System.nanoTime(); 
     TestingEnum[] values = TestingEnum.values(); 
     for (TestingEnum v : values) { 
      if (v.toString().equals(checkFor)) { 
       timeEnd = System.nanoTime(); 
       timeIterate += timeEnd - timeStart; 
       return true; 
      } 
     } 
     timeEnd = System.nanoTime(); 
     timeIterate += timeEnd - timeStart; 
     return false; 
    } 
} 

इसका मतलब यह है, प्रत्येक तरीकों 50000 बार enum की लंबाई मैं 10 से अधिक बार इस परीक्षा में भाग गया, चलाने के लिए, 20, 50 और 100 enum स्थिरांक । यहां परिणाम हैं:

  • 10: कोशिश करें/पकड़ें: 760ms | पुनरावृत्ति: 62ms
  • 20: कोशिश करें/पकड़ें: 1671ms | पुनरावृत्ति: 177ms
  • 50: कोशिश करें/पकड़ें: 3113ms | पुनरावृत्ति: 488ms
  • 100: कोशिश करें/पकड़ें: 6834ms | पुनरावृत्ति: 1760ms

ये परिणाम सटीक नहीं थे। इसे फिर से निष्पादित करते समय, परिणामों में 10% अंतर होता है, लेकिन वे दिखाने के लिए पर्याप्त हैं, कि प्रयास/पकड़ विधि बहुत कम कुशल है, खासकर छोटे enums के साथ।