सरणी [idx ++] + = "a" idx को एक बार जावा 8 में बढ़ाता है, लेकिन जावा 9 और 10 में दो बार क्यों करता है?


751

एक चुनौती के लिए, एक साथी कोड गोल्फर ने निम्नलिखित कोड लिखा :

import java.util.*;
public class Main {
  public static void main(String[] args) {
    int size = 3;
    String[] array = new String[size];
    Arrays.fill(array, "");
    for(int i = 0; i <= 100; ) {
      array[i++%size] += i + " ";
    }
    for(String element: array) {
      System.out.println(element);
    }
  }
}

जावा 8 में इस कोड को चलाने पर, हमें निम्न परिणाम मिलते हैं:

1 4 7 10 13 16 19 22 25 28 31 34 37 40 43 46 49 52 55 58 61 64 67 70 73 76 79 82 85 88 91 94 97 100 
2 5 8 11 14 17 20 23 26 29 32 35 38 41 44 47 50 53 56 59 62 65 68 71 74 77 80 83 86 89 92 95 98 101 
3 6 9 12 15 18 21 24 27 30 33 36 39 42 45 48 51 54 57 60 63 66 69 72 75 78 81 84 87 90 93 96 99 

जावा 10 में इस कोड को चलाने पर, हमें निम्न परिणाम मिलते हैं:

2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 46 48 50 52 54 56 58 60 62 64 66 68 70 72 74 76 78 80 82 84 86 88 90 92 94 96 98 
2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 46 48 50 52 54 56 58 60 62 64 66 68 70 72 74 76 78 80 82 84 86 88 90 92 94 96 98 100 102 
2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 46 48 50 52 54 56 58 60 62 64 66 68 70 72 74 76 78 80 82 84 86 88 90 92 94 96 98 100 

नंबर 10 जावा के उपयोग से पूरी तरह से बंद है। तो यहां क्या हो रहा है? यह जावा 10 में एक बग है?

टिप्पणियों से अप का पालन करें:

  • जब जावा 9 या बाद में संकलित किया जाता है (तो हमने इसे जावा 10 में पाया)। इस कोड को जावा 8 पर संकलित करना, फिर जावा 9 या किसी भी बाद के संस्करण में चलना, जिसमें जावा 11 जल्दी पहुंच शामिल है, अपेक्षित परिणाम देता है।
  • इस तरह का कोड गैर-मानक है, लेकिन युक्ति के अनुसार मान्य है। यह केविन क्रूज़सेन द्वारा एक गोल्फ चुनौती में एक चर्चा में पाया गया था , इसलिए अजीब उपयोग के मामले का सामना करना पड़ा।
  • डिडियर एल को पता चला कि इस मुद्दे को बहुत छोटे और अधिक समझने योग्य कोड के साथ पुन: प्रस्तुत किया जा सकता है:

    class Main {
      public static void main(String[] args) {
        String[] array = { "" };
        array[test()] += "a";
      }
      static int test() {
        System.out.println("evaluated");
        return 0;
      }
    }

    जावा 8 में संकलित होने पर परिणाम:

    evaluated

    जावा 9 और 10 में संकलित होने पर परिणाम:

    evaluated
    evaluated
  • यह समस्या स्ट्रिंग कॉन्फैटेनेशन और असाइनमेंट ऑपरेटर ( +=) के साथ साइड इफेक्ट के रूप में एक अभिव्यक्ति के साथ सीमित होती है , जो कि array[test()]+="a", जैसे , या array[ix++]+="a", में बाएं संकार्य के रूप में सीमित लगती है । स्ट्रिंग संघनन को सक्षम करने के लिए, कम से कम एक पक्ष का प्रकार होना चाहिए । अन्य प्रकार या निर्माणों पर इसे पुन: पेश करने की कोशिश विफल रही।test()[index]+="a"test().field+="a"String


5
टिप्पणियाँ विस्तारित चर्चा के लिए नहीं हैं; इस वार्तालाप को बातचीत में स्थानांतरित कर दिया गया है ।
शमूएल एलवाई

13
@ जॉली जोकर इसे +=अप्रत्यक्ष Stringसंदर्भों पर लागू करने तक सीमित है । तो पहले, आपकी सरणी एक होनी चाहिए String[]। समस्या नहीं होती है int[], long[]और मित्र। लेकिन हाँ, आप मूल रूप से सही हैं!
ओलिवियर ग्रेगोइरे

2
@ OlivierGrégoire सरणी होने की आवश्यकता नहीं है String[]। अगर यह है Object[]और आप करते हैंarray[expression] += "foo"; , यह समान है। लेकिन हाँ, यह आदिम सरणियों पर लागू नहीं होता है, क्योंकि यह प्रकार के संदर्भ धारण करने के लिए सक्षम होना चाहिए String( Object[], CharSequence[], Comparable[], ...), स्ट्रिंग संयोजन का परिणाम स्टोर करने के लिए।
होल्गर

30
इसे बग आईडी JDK-8204322 सौंपा गया है ।
स्टुअर्ट मार्क्स

1
@StuartMarks धन्यवाद! यह जवाब में एकीकृत किया गया है: मैं वास्तव में सवाल को एक सवाल रखना चाहता था, चाहे वह सामान्य हो या बग। हालाँकि, हम उत्तर में बग की आईडी के बारे में अधिक स्पष्ट हो सकते हैं। मैं इसे अभी अनुकूलित करूंगा।
ओलिवियर ग्रेजायर

जवाबों:


625

यह javacJDK 9 से शुरू होने वाला एक बग है (जिसमें स्ट्रिंग के संगति के संबंध में कुछ बदलाव किए गए थे, जो मुझे संदेह है कि यह समस्या का हिस्सा है), जैसा javacकि बग आईडी JDK-8204322 के तहत टीम द्वारा पुष्टि की गई है । यदि आप पंक्ति के लिए इसी बायोटेक को देखते हैं:

array[i++%size] += i + " ";

यह है:

  21: aload_2
  22: iload_3
  23: iinc          3, 1
  26: iload_1
  27: irem
  28: aload_2
  29: iload_3
  30: iinc          3, 1
  33: iload_1
  34: irem
  35: aaload
  36: iload_3
  37: invokedynamic #5,  0 // makeConcatWithConstants:(Ljava/lang/String;I)Ljava/lang/String;
  42: aastore

जहां अंतिम aaloadसरणी से वास्तविक भार है। हालाँकि, भाग

  21: aload_2             // load the array reference
  22: iload_3             // load 'i'
  23: iinc          3, 1  // increment 'i' (doesn't affect the loaded value)
  26: iload_1             // load 'size'
  27: irem                // compute the remainder

जो मोटे तौर पर अभिव्यक्ति से मेल खाती है array[i++%size](वास्तविक भार और दुकान का ऋण), वहां दो बार है। यह गलत है, जैसा कि jls-15.26.2 में कल्पना कहती है :

प्रपत्र E1 op= E2का एक मिश्रित असाइनमेंट अभिव्यक्ति के बराबर है E1 = (T) ((E1) op (E2)), जहां Tका प्रकार हैE1 , सिवाय इसके कि E1केवल एक बार मूल्यांकन किया जाता है।

इसलिए, अभिव्यक्ति के लिए array[i++%size] += i + " ";, भाग array[i++%size]का केवल एक बार मूल्यांकन किया जाना चाहिए। लेकिन इसका मूल्यांकन दो बार किया जाता है (एक बार लोड के लिए, और एक बार स्टोर के लिए)।

तो हाँ, यह एक बग है।


कुछ अपडेट:

बग JDK 11 में तय किया गया है और JDK 10 का बैक-पोर्ट होगा (लेकिन JDK 9 नहीं) यह अब सार्वजनिक अपडेट प्राप्त नहीं करता है )।

जेबे पेज पर अलेक्सी शिपिलेव का उल्लेख है (और @DidierL यहाँ टिप्पणियों में) :

वर्कअराउंड: के साथ संकलन -XDstringConcat=inline

यह समवर्ती करने के लिए उपयोग StringBuilderकरने के लिए वापस आ जाएगा , और बग नहीं है।


34
वैसे, यह पूरे बाएं हाथ की अभिव्यक्ति पर लागू होता है, न केवल सूचकांक उप-अभिव्यक्ति प्रदान करता है। यह अभिव्यक्ति मनमाने ढंग से जटिल हो सकती है। उदाहरण के लिए देखें IntStream.range(0, 10) .peek(System.out::println).boxed().toArray()[0] += "";...
Holger

9
@Holger बाएं हाथ की ओर भी सरणियों को शामिल करने की आवश्यकता नहीं है, मुद्दा भी एक सरल के साथ होता है test().field += "sth"
दिदिर एल

44
ऐसा नहीं है कि यह मायने रखता है, व्यवहार वैसे भी बुरी तरह से टूट गया है, लेकिन पहला मूल्यांकन स्टोर और लोड के लिए दूसरा है, इसलिए array[index++] += "x";इसे पढ़ने array[index+1]और लिखने के लिए array[index]...
होल्टर

5
@ TheCoder हाँ मुझे ऐसा लगता है। JDK 9 एक दीर्घकालिक समर्थन (LTS) रिलीज़ नहीं है। JDK 8 था, और अगला LTS रिलीज़ JDK 11 है। यहाँ देखें: oracle.com/technetwork/java/javase/eol-135779.html ध्यान दें कि JDK 9 का सार्वजनिक अपडेट मार्च में समाप्त हो गया था।
जोर्न वर्नी

15
JDK-8204322 पर, अलेक्सी शिपिलेव ने -XDstringConcat=inlineउन लोगों के लिए एक वर्कअराउंड के रूप में संकलन करने का सुझाव दिया , जिन्हें इसकी आवश्यकता है।
डिडिएर एल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.