2014-08-29 15 views
17

मैंने JVM specification को संकलन संकलन पर पढ़ा और स्ट्रिंग पर स्विच स्टेटमेंट संकलित करने में रुचि रखने में दिलचस्पी दिखाई। यहाँ परीक्षा पद्धति मैं जांच (JDK1.7.0_40) है:स्ट्रिंग संकलन को दो स्विच में स्विच क्यों करें

static int test(String i) { 
    switch (i) { 
     case "a": return -100; 
     case "45b": return 1; 
     case "c": return 2; 
     default: return -1; 
    } 
} 

मैं इस विधि स्ट्रिंग के hashCode पर सरल lookupswitch में संकलित होने की उम्मीद है, लेकिन अचानक

static int test(java.lang.String); 
Code: 
    0: aload_0 
    1: astore_1 
    2: iconst_m1 
    3: istore_2 
    4: aload_1 
    5: invokevirtual #6   // Method java/lang/String.hashCode:()I 
    8: lookupswitch { // 3 
       97: 44 
       99: 72 
      51713: 58 
      default: 83 
     } 
    44: aload_1 
    45: ldc   #7 // String a 
    47: invokevirtual #8 // Method java/lang/String.equals:(Ljava/lang/Object;)Z 
    50: ifeq   83 
    53: iconst_0 
    54: istore_2 
    55: goto   83 
    58: aload_1 
    59: ldc   #9 // String 45b 
    61: invokevirtual #8 // Method java/lang/String.equals:(Ljava/lang/Object;)Z 
    64: ifeq   83 
    67: iconst_1 
    68: istore_2 
    69: goto   83 
    72: aload_1 
    73: ldc   #10 // String c 
    75: invokevirtual #8 // Method java/lang/String.equals:(Ljava/lang/Object;)Z 
    78: ifeq   83 
    81: iconst_2 
    82: istore_2 
    83: iload_2 
    84: tableswitch { // 0 to 2 
       0: 112 
       1: 115 
       2: 117 
      default: 119 
     } 
112: bipush  -100 
114: ireturn 
115: iconst_1 
116: ireturn 
117: iconst_2 
118: ireturn 
119: iconst_m1 
120: ireturn 

आप देख सकते हैं के रूप में, पहली लुकअपविच की शाखाओं में जेवीएम इसके बाद के बाद के टेबलविच (लाइन 84) के लिए वास्तविक उत्पन्न करने वाले इंडेक्स नहीं कर रहा है।
टेबल्सविच को तेजी से काम करना चाहिए ताकि बहुत अधिक काम नहीं मिल सके। लेकिन वैसे भी, अतिरिक्त स्विच उत्पन्न करने का उद्देश्य क्या है?
अद्यतन
मैं हैशकोड टकराव की संभावना को समझता हूं। मैं जो कहने की कोशिश कर रहा हूं वह यह है कि बाद के टेबलविच की बजाय, कंपाइलर सभी असली काम को बाद में टेबलविच से पहले स्थानांतरित कर सकता है और फिर सभी स्विच शाखाओं के अंत तक कूदने के लिए ifeq का उपयोग कर सकता है। तो एक संभावित उत्तर मैं यहां देखता हूं: कि पहले स्विच कंपाइलर में मामलों की ज्ञात संख्या के आधार पर ifeq कूद के लिए लेबल को प्रीकंप्यूट करने का प्रयास करता है, लेकिन मुझे यकीन नहीं है कि यह एकमात्र कारण है।

Update2
@ericbn के रूप में सुझाव दिया है, मैं के रूप में पूर्णांक और संकलक मुझे सादा lookupswitch दिया मैं के साथ

switch (i) { 
    case 97: return -100; 
    case 51713: return 1; 
    case 99: return 2; 
    default: return -1; 
} 

संकलन करने की कोशिश की।

+0

विशिष्ट स्ट्रिंग (रों) कबूतर का घोंसला सिद्धांत के कारण एक ही hashCode हो सकता है। मुझे लगता है कि यही कारण है कि यह दो स्विच (एसएस) करता है। –

+2

क्या आपको नहीं लगता कि केवल एक स्विच है? दूसरा स्विच सशर्त वापसी के लिए है। – HuStmpHrrr

+1

@HuStmpHrrr दूसरा स्विच संकेत की स्पष्ट रूप से अप्रचलित परत पेश करता है, तालिका से देखा गया कूद जो पहले स्विच ब्लॉक में 'गोटो' लक्ष्य के रूप में गंभीर रूप से डाला जा सकता था। –

उत्तर

11

javac source code से अदालत में तलब:

  * The general approach used is to translate a single 
     * string switch statement into a series of two chained 
     * switch statements: the first a synthesized statement 
     * switching on the argument string's hash value and 
     * computing a string's position in the list of original 
     * case labels, if any, followed by a second switch on the 
     * computed integer value. The second switch has the same 
     * code structure as the original string switch statement 
     * except that the string case labels are replaced with 
     * positional integer constants starting at 0. 
     * 
     * The first switch statement can be thought of as an 
     * inlined map from strings to their position in the case 
     * label list. An alternate implementation would use an 
     * actual Map for this purpose, as done for enum switches. 
     * 
     * With some additional effort, it would be possible to 
     * use a single switch statement on the hash code of the 
     * argument, but care would need to be taken to preserve 
     * the proper control flow in the presence of hash 
     * collisions and other complications, such as 
     * fallthroughs. Switch statements with one or two 
     * alternatives could also be specially translated into 
     * if-then statements to omit the computation of the hash 
     * code. 
संबंधित मुद्दे