स्ट्रिंग जावा में अपरिवर्तनीय क्यों है?


78

मैं इसका कारण नहीं समझ सका। मैं हमेशा अन्य डेवलपर्स की तरह स्ट्रिंग क्लास का उपयोग करता हूं, लेकिन जब मैं इसके मूल्य को संशोधित करता हूं, तो स्ट्रिंग का नया उदाहरण बनाया जाता है।

जावा में स्ट्रिंग वर्ग के लिए अपरिवर्तनीयता का कारण क्या हो सकता है?

मुझे पता है कि स्ट्रिंगरबफ़र या स्ट्रिंगबर्ल जैसे कुछ विकल्प हैं। यह सिर्फ जिज्ञासा है।


20
तकनीकी रूप से, यह कोई डुप्लिकेट नहीं है, लेकिन एरिक
लिपर्ट

जवाबों:


105

संगामिति

जावा को शुरू से ही संगामिति के विचारों से परिभाषित किया गया था। जैसा कि अक्सर उल्लेख किया गया है कि साझा उत्परिवर्ती समस्याग्रस्त हैं। एक चीज दूसरे धागे की पीठ के पीछे एक और परिवर्तन कर सकती है बिना उस धागे के बारे में पता चलेगा।

मल्टीथ्रेडेड C ++ बग्स के एक मेजबान हैं जो एक साझा स्ट्रिंग की वजह से तैयार हुए हैं - जहां एक मॉड्यूल ने सोचा कि यह बदलना सुरक्षित है जब कोड में एक अन्य मॉड्यूल ने एक पॉइंटर को इसे सहेजा था और इसे उसी रहने की उम्मीद थी।

इसका 'समाधान' यह है कि हर वर्ग उन उत्परिवर्तनीय वस्तुओं की एक रक्षात्मक प्रतिलिपि बनाता है, जो उसमें पारित हो जाती हैं। परिवर्तनशील स्ट्रिंग्स के लिए, यह प्रतिलिपि बनाने के लिए O (n) है। अपरिवर्तनीय स्ट्रिंग्स के लिए, एक प्रतिलिपि बनाना O (1) है क्योंकि यह एक कॉपी नहीं है, इसकी वही वस्तु है जो बदल नहीं सकती है।

एक बहुपरत वातावरण में, अपरिवर्तनीय वस्तुओं को हमेशा एक दूसरे के बीच सुरक्षित रूप से साझा किया जा सकता है। यह स्मृति उपयोग में समग्र कमी की ओर जाता है और मेमोरी कैशिंग में सुधार करता है।

सुरक्षा

कई बार स्ट्रिंगरों को कंस्ट्रक्टरों के तर्क के रूप में पास किया जाता है - नेटवर्क कनेक्शन और प्रोटोकाॅल दो ऐसे हैं जो सबसे आसानी से दिमाग में आते हैं। निष्पादन में बाद में एक अनिर्धारित समय पर इसे बदलने में सक्षम होने के कारण सुरक्षा संबंधी समस्याएं हो सकती हैं (फ़ंक्शन ने सोचा कि यह एक मशीन से कनेक्ट हो रहा था, लेकिन दूसरे को डायवर्ट कर दिया गया था, लेकिन ऑब्जेक्ट में सब कुछ ऐसा लगता है जैसे यह पहले से जुड़ा हुआ है ... इसकी भी एक ही स्ट्रिंग)।

जावा एक प्रतिबिंब का उपयोग करने देता है - और इसके लिए पैरामीटर स्ट्रिंग हैं। एक स्ट्रिंग को पारित करने का खतरा जो प्रतिबिंबित करने के तरीके के माध्यम से संशोधित हो सकता है। यह बहुत बुरा है।

हास की कुंजी

हैश तालिका सबसे अधिक उपयोग की जाने वाली डेटा संरचनाओं में से एक है। डेटा संरचना की कुंजी अक्सर तार होती है। अपरिवर्तनीय तार होने का मतलब है कि (ऊपर के रूप में) हैश तालिका को हर बार हैश कुंजी की प्रतिलिपि बनाने की आवश्यकता नहीं है। यदि तार परस्पर थे, और हैश तालिका ने ऐसा नहीं किया, तो कुछ के लिए दूरी पर हैश कुंजी को बदलना संभव होगा।

जिस तरह से जावा में ऑब्जेक्ट काम करता है, वह यह है कि सब कुछ एक हैश कुंजी (हैशकोड () विधि के माध्यम से पहुँचा) है। एक अपरिवर्तनीय स्ट्रिंग होने का मतलब है कि हैशकोड को कैश किया जा सकता है। यह देखते हुए कि स्ट्रिंग्स को कितनी बार हैश की कुंजी के रूप में उपयोग किया जाता है, यह एक महत्वपूर्ण प्रदर्शन को बढ़ावा देता है (हर बार हैश कोड को पुनर्गणना करने के बजाय)।

सबस्ट्रिंग

स्ट्रिंग अपरिवर्तनीय होने से, डेटा संरचना को बैक करने वाला अंतर्निहित वर्ण सरणी भी अपरिवर्तनीय है। यह किए जाने वाले substringतरीके पर कुछ अनुकूलन के लिए अनुमति देता है (वे जरूरी नहीं हैं - यह कुछ मेमोरी लीक की संभावना का भी परिचय देता है)।

यदि तुम करो:

String foo = "smiles";
String bar = foo.substring(1,5);

का मान bar'मील' है। हालाँकि, दोनों को fooऔर barएक ही कैरेक्टर एरे द्वारा समर्थित किया जा सकता है, और अधिक कैरेक्टर एरे की तात्कालिकता को कम करना या इसे कॉपी करना - बस स्ट्रिंग के भीतर अलग-अलग स्टार्ट और एंड पॉइंट का उपयोग करना।

फू | | (0, 6)
    vv
    मुस्कान
     ^ ^
बार | | (1, 5)

अब, उस (मेमोरी लीक) के नकारात्मक पक्ष यह है कि अगर किसी के पास 1k लंबी स्ट्रिंग थी और उसने पहले और दूसरे चरित्र का विकल्प लिया, तो यह 1k लंबे चरित्र सरणी द्वारा भी समर्थित होगा। यह सरणी स्मृति में बनी रहेगी भले ही मूल स्ट्रिंग जिसमें संपूर्ण वर्ण सरणी का मान था कचरा एकत्रित किया गया था।

इसे JDK 6b14 से स्ट्रिंग में देख सकते हैं (निम्नलिखित कोड GPL v2 स्रोत से है और उदाहरण के रूप में उपयोग किया जाता है)

   public String(char value[], int offset, int count) {
       if (offset < 0) {
           throw new StringIndexOutOfBoundsException(offset);
       }
       if (count < 0) {
           throw new StringIndexOutOfBoundsException(count);
       }
       // Note: offset or count might be near -1>>>1.
       if (offset > value.length - count) {
           throw new StringIndexOutOfBoundsException(offset + count);
       }
       this.offset = 0;
       this.count = count;
       this.value = Arrays.copyOfRange(value, offset, offset+count);
   }

   // Package private constructor which shares value array for speed.
   String(int offset, int count, char value[]) {
       this.value = value;
       this.offset = offset;
       this.count = count;
   }

   public String substring(int beginIndex, int endIndex) {
       if (beginIndex < 0) {
           throw new StringIndexOutOfBoundsException(beginIndex);
       }
       if (endIndex > count) {
           throw new StringIndexOutOfBoundsException(endIndex);
       }
       if (beginIndex > endIndex) {
           throw new StringIndexOutOfBoundsException(endIndex - beginIndex);
       }
       return ((beginIndex == 0) && (endIndex == count)) ? this :
           new String(offset + beginIndex, endIndex - beginIndex, value);
   }

ध्यान दें कि विकल्प पैकेज स्तर के स्ट्रिंग स्ट्रिंग कंस्ट्रक्टर का उपयोग कैसे करता है जिसमें सरणी की कोई भी प्रतिलिपि शामिल नहीं होती है और यह बहुत तेज़ होगा (संभवतः कुछ बड़े सरणियों के आसपास रखने की कीमत पर - हालांकि बड़े सरणियों की नकल नहीं कर रहा है)।

ध्यान दें कि उपरोक्त कोड जावा 1.6 के लिए है। जिस तरह से सबस्ट्रिंग कंस्ट्रक्टर को लागू किया गया है, उसे जावा 1.7 के साथ बदल दिया गया था क्योंकि परिवर्तन के रूप में जावा 1.7.0_06 में किए गए आंतरिक प्रतिनिधित्व को स्ट्रिंग में प्रलेखित किया गया था - उस मेमोरी लीक को काटने वाला मुद्दा जो मैंने ऊपर उल्लेख किया था। जावा संभावना को स्ट्रिंग हेरफेर के बहुत से भाषा के रूप में नहीं देखा गया था और इसलिए एक विकल्प के लिए प्रदर्शन को बढ़ावा देना एक अच्छी बात थी। अब, स्ट्रिंग में संग्रहीत विशाल XML दस्तावेज़ों के साथ जो कभी भी एकत्र नहीं किए जाते हैं, यह एक मुद्दा बन जाता है ... और इस प्रकार Stringएक विकल्प के साथ एक ही अंतर्निहित सरणी का उपयोग नहीं करने के लिए परिवर्तन , ताकि बड़े चरित्र सरणी को अधिक तेज़ी से एकत्र किया जा सके।

स्टैक का दुरुपयोग न करें

एक सकता है के बजाय चारों ओर अपरिवर्तनीय स्ट्रिंग के संदर्भ में अस्थिरता के साथ मुद्दों से बचने के लिए तार का मूल्य गुजरती हैं। हालाँकि, बड़े स्ट्रिंग्स के साथ, इसे स्टैक पर पास करना ... सिस्टम के लिए अपमानजनक होगा (स्टैक पर स्ट्रिंग्स पर पूरे xml डॉक्यूमेंट्स डालना और फिर उन्हें उतारना या उन्हें साथ रखना जारी रखना ...)।

कटौती की संभावना

दी, यह एक प्रारंभिक प्रेरणा नहीं थी कि स्ट्रिंग्स को अपरिवर्तनीय क्यों बनाया जाना चाहिए, लेकिन जब कोई तर्कसंगत है कि अपरिवर्तनीय स्ट्रिंग्स एक अच्छी चीज क्यों है, तो यह निश्चित रूप से विचार करने के लिए कुछ है।

जिसने भी स्ट्रिंग्स के साथ काम किया है, वह जानता है कि वे मेमोरी चूस सकते हैं। यह विशेष रूप से सच है जब आप डेटाबेस से डेटा खींचने जैसे काम कर रहे होते हैं जो थोड़ी देर के लिए चिपक जाते हैं। कई बार इन डंक के साथ, वे एक ही स्ट्रिंग बार-बार होते हैं (प्रत्येक पंक्ति के लिए एक बार)।

कई बड़े पैमाने पर जावा एप्लिकेशन वर्तमान में मेमोरी पर अड़चन हैं। माप से पता चला है कि इस प्रकार के अनुप्रयोगों में लगभग 25% जावा हीप लाइव डेटा सेट किया जाता है, जिसका उपयोग स्ट्रिंग ऑब्जेक्ट द्वारा किया जाता है। इसके अलावा, स्ट्रिंग वस्तुओं में से लगभग आधे डुप्लिकेट हैं, जहां डुप्लिकेट का अर्थ है string1.equals (string2) सच है। डुप्लिकेट स्ट्रिंग वस्तुओं को ढेर पर रखना, अनिवार्य रूप से, बस स्मृति की बर्बादी है। ...

जावा 8 अपडेट 20 के साथ, जेईपी 192 (ऊपर उद्धृत प्रेरणा) को संबोधित करने के लिए लागू किया जा रहा है। स्ट्रिंग समर्पण कैसे काम करता है, इसका विवरण प्राप्त किए बिना, यह आवश्यक है कि स्ट्रिंग्स स्वयं अपरिवर्तनीय हैं। आप StringBuilders को घटा नहीं सकते क्योंकि वे बदल सकते हैं और आप नहीं चाहते कि कोई आपके नीचे से कुछ बदले। अपरिवर्तनीय स्ट्रिंग्स (उस स्ट्रिंग पूल से संबंधित) का मतलब है कि आप के माध्यम से जा सकते हैं और यदि आपको दो तार मिलते हैं जो समान हैं, तो आप एक स्ट्रिंग संदर्भ को दूसरे को इंगित कर सकते हैं और कचरा कलेक्टर को नए अप्रयुक्त का उपभोग करने दें।

अन्य भाषाएँ

ऑब्जेक्टिव सी (जो जावा को दर्शाता है) NSStringऔर NSMutableString

C # और .NET ने डिफ़ॉल्ट स्ट्रिंग के एक ही डिज़ाइन विकल्प को अपरिवर्तनीय बनाया।

लुआ के तार भी अपरिवर्तनीय हैं।

साथ ही अजगर

ऐतिहासिक रूप से, लिस्प, स्कीम, स्मॉलटॉक सभी ने स्ट्रिंग को इंटर्न किया है और इस तरह यह अपरिवर्तनीय है। अधिक आधुनिक गतिशील भाषाएं अक्सर किसी न किसी तरह से तार का उपयोग करती हैं जिसके लिए उन्हें अपरिवर्तनीय होना चाहिए (यह एक स्ट्रिंग नहीं हो सकता है , लेकिन यह अपरिवर्तनीय है)।

निष्कर्ष

ये डिज़ाइन विचार बार-बार भाषाओं की भीड़ में बनाए गए हैं। यह आम सहमति है कि अपरिवर्तनीय तार, उनके सभी अजीबता के लिए, विकल्पों से बेहतर हैं और बेहतर कोड (कम बग) और तेजी से निष्पादन योग्य समग्र करने के लिए नेतृत्व करते हैं।


3
जावा परस्पर और अपरिवर्तनीय तार प्रदान करता है। यह उत्तर कुछ प्रदर्शन लाभों का विवरण देता है, जिन्हें अपरिवर्तनीय स्ट्रिंग्स पर किया जा सकता है, और कुछ कारणों से अपरिवर्तनीय डेटा का चयन किया जा सकता है; लेकिन चर्चा नहीं करता है कि अपरिवर्तनीय संस्करण डिफ़ॉल्ट संस्करण क्यों है।
बिली ओनेल

3
@ बिलियन: एक सुरक्षित डिफ़ॉल्ट और एक असुरक्षित विकल्प लगभग हमेशा विपरीत दृष्टिकोण की तुलना में अधिक सुरक्षित प्रणालियों की ओर जाता है।
जोकिम सॉयर

4
@Billyoneal यदि अपरिवर्तनीय समसामयिकता, सुरक्षा, और हैश के मुद्दे डिफ़ॉल्ट नहीं थे, तो यह अधिक सामान्य होगा। भाषा डिजाइनरों ने एक भाषा बनाने के लिए (सी के जवाब में कुछ समय के लिए) चुना है, जहां प्रोग्रामर दक्षता में सुधार करने के लिए कई सामान्य बग को रोकने के लिए प्रयास करने के लिए चूक की स्थापना की जाती है (अब इन बगों के बारे में चिंता नहीं करनी चाहिए)। उत्परिवर्तित स्ट्रिंग्स के साथ कम बग (स्पष्ट और छिपे हुए) होते हैं जो म्यूटेबल वाले होते हैं।

@ जोशीम: मैं अन्यथा दावा नहीं कर रहा हूं।
बिली ओनेल

1
तकनीकी रूप से, कॉमन लिस्प में "स्ट्रिंग-लाइक" संचालन के लिए और अपरिवर्तनीय पहचानकर्ताओं के लिए अपरिवर्तनीय नामों के प्रतीक हैं।
वेटिन

21

कारण मैं याद कर सकते हैं:

  1. स्ट्रिंग अपरिवर्तनीय बनाने के बिना स्ट्रिंग पूल की सुविधा बिल्कुल भी संभव नहीं है क्योंकि स्ट्रिंग पूल के मामले में एक स्ट्रिंग ऑब्जेक्ट / शाब्दिक जैसे "XYZ" को कई संदर्भ चर द्वारा संदर्भित किया जाएगा, इसलिए यदि उनमें से कोई एक मूल्य बदलता है तो अन्य स्वचालित रूप से प्रभावित होंगे। ।

  2. स्ट्रिंग को व्यापक रूप से कई जावा वर्गों के लिए पैरामीटर के रूप में उपयोग किया जाता है, जैसे नेटवर्क कनेक्शन खोलने के लिए, डेटाबेस कनेक्शन खोलने के लिए, फाइलें खोलने के लिए। अगर स्ट्रिंग अपरिवर्तनीय नहीं है, तो इससे गंभीर सुरक्षा खतरा होगा।

  3. अपरिवर्तनीयता स्ट्रिंग को अपने हैशकोड को कैश करने की अनुमति देता है।

  4. इसे धागा-सुरक्षित बनाता है।


7

1) स्ट्रिंग पूल

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

String s1 = "Java";
String s2 = "Java";

अब यदि s1 ऑब्जेक्ट को "Java" से "C ++" में बदलता है, तो रेफरेंस वैरिएबल को भी मान s2 = "C ++" मिला, जिसके बारे में उसे पता भी नहीं है। स्ट्रिंग को अपरिवर्तनीय बनाकर, स्ट्रिंग शाब्दिक का यह साझाकरण संभव था। संक्षेप में, स्ट्रिंग पूल के प्रमुख विचार को स्ट्रिंग के अंतिम या जावा में अपरिवर्तनीय बनाने के बिना लागू नहीं किया जा सकता है।

2) सुरक्षा

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

3) कक्षा लोडिंग तंत्र में स्ट्रिंग का उपयोग

स्ट्रिंग को अंतिम या अपरिवर्तनीय बनाने का एक अन्य कारण इस तथ्य से प्रेरित था कि इसका उपयोग कक्षा लोडिंग तंत्र में भारी रूप से किया गया था। जैसा कि स्ट्रिंग अपरिवर्तनीय नहीं है, एक हमलावर इस तथ्य का लाभ उठा सकता है और मानक जावा कक्षाओं को लोड करने का अनुरोध कर सकता है जैसे java.io.Reader को दुर्भावनापूर्ण वर्ग com.unknown.DataStolenReader में बदला जा सकता है। स्ट्रिंग अंतिम और अपरिवर्तनीय रखकर, हम कम से कम यह सुनिश्चित कर सकते हैं कि जेवीएम सही कक्षाएं लोड कर रहा है।

4) बहुपरत लाभ

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

5) अनुकूलन और प्रदर्शन

अब जब आप एक वर्ग को अपरिवर्तनीय बनाते हैं, तो आप पहले से जानते हैं कि, यह वर्ग एक बार निर्मित होने वाला नहीं है। यह कई प्रदर्शन अनुकूलन जैसे कैशिंग के लिए खुले मार्ग की गारंटी देता है। स्ट्रिंग ही जानता है कि, मैं बदलने नहीं जा रहा हूँ, इसलिए स्ट्रिंग ने अपने हैशकोड को कैश किया। यहां तक ​​कि यह हैशकोड आलसी की गणना करता है और एक बार बनाया गया है, बस इसे कैश करें। सरल दुनिया में, जब आप पहली बार किसी भी स्ट्रिंग ऑब्जेक्ट के हैशकोड () विधि को कॉल करते हैं, तो यह हैश कोड की गणना करता है और सभी बाद की कॉल हैशकोड () पहले से गणना की गई रिटर्न, कैश्ड वैल्यू की गणना करता है। यह अच्छा प्रदर्शन लाभ देता है, दिया गया स्ट्रिंग हैश आधारित मैप्स जैसे हैशटेबल और हैशपैप में भारी उपयोग किया जाता है। हैशकोड का कैशिंग अपरिवर्तनीय और अंतिम बनाने के बिना संभव नहीं था, क्योंकि यह स्ट्रिंग की सामग्री पर निर्भर करता है।


5

जावा वर्चुअल मशीन स्ट्रिंग संचालन के बारे में कई अनुकूलन करता है जो अन्यथा नहीं किया जा सकता है। उदाहरण के लिए, यदि आपके पास "मिसिसिपी" के साथ एक स्ट्रिंग है और आपने "मिसिसिपी" को सौंपा है। किसी अन्य स्ट्रिंग के लिए (0, 4)। जहाँ तक आप जानते हैं, "मिस" बनाने के लिए पहले चार पात्रों में से एक प्रतिलिपि बनाई गई थी। । आप क्या नहीं जानते कि दोनों एक ही मूल स्ट्रिंग "मिसिसिपी" साझा करते हैं, एक मालिक होने के साथ और दूसरा उस स्ट्रिंग का संदर्भ स्थिति 0 से 4 तक है (स्वामी का संदर्भ स्वामी को एकत्रित होने से रोकता है कूड़ा उठाने वाला जब मालिक के दायरे से बाहर हो जाता है)

यह "मिसिसिपी" के रूप में एक स्ट्रिंग के लिए तुच्छ है, लेकिन बड़े तार और कई संचालन के साथ, स्ट्रिंग की नकल करने के लिए नहीं एक बड़ा समय बचाने वाला है! यदि तार परस्पर भिन्न होते थे, तो आप ऐसा नहीं कर सकते थे, क्योंकि मूल को संशोधित करने से विकल्प "प्रतियाँ" भी प्रभावित होंगी।

इसके अलावा, जैसा कि डोनल ने उल्लेख किया है, इसका लाभ इसके नुकसान से बहुत कम होगा। कल्पना कीजिए कि आप एक कार्यक्रम लिखते हैं जो एक पुस्तकालय पर निर्भर करता है और आप एक फ़ंक्शन का उपयोग करते हैं जो एक स्ट्रिंग लौटाता है। आप यह कैसे सुनिश्चित कर सकते हैं कि मूल्य स्थिर रहेगा? यह सुनिश्चित करने के लिए कि ऐसा कुछ न हो, आपको हमेशा एक कॉपी तैयार करनी होगी।

क्या होगा यदि आपके पास एक ही स्ट्रिंग साझा करने वाले दो धागे हैं? आप एक स्ट्रिंग पढ़ना नहीं चाहेंगे जो वर्तमान में एक और धागे द्वारा फिर से लिखा जा रहा है, क्या आप करेंगे? स्ट्रिंग तो इसलिए थ्रेड सुरक्षित होना चाहिए, जो कि सामान्य वर्ग है कि यह है, लगभग हर जावा प्रोग्राम को इतना धीमा बना देगा। अन्यथा, आपको प्रत्येक थ्रेड के लिए एक प्रतिलिपि बनानी होगी जिसमें उस स्ट्रिंग की आवश्यकता होती है या आपको उस स्ट्रिंग का उपयोग करके कोड को एक सिंक्रोनाइज़ ब्लॉक में रखना होगा, दोनों ही आपके प्रोग्राम को धीमा कर देते हैं।

इन सभी कारणों के लिए, यह जावा के लिए प्रारंभिक निर्णयों में से एक था, ताकि सी ++ से खुद को अलग किया जा सके।


सैद्धांतिक रूप से, आप एक बहु-परत बफर प्रबंधन कर सकते हैं जो कॉपी-ऑन-म्यूटेशन-अगर-साझा करने की अनुमति देता है, लेकिन यह बहु-थ्रेडेड वातावरण में कुशलतापूर्वक काम करने के लिए बहुत कठिन है।
डोनल फैलो

@DonalFellows मैंने अभी यह अनुमान लगाया है कि चूंकि जावा वर्चुअल मशीन जावा (स्पष्ट रूप से) में नहीं लिखी गई है, इसे आंतरिक रूप से साझा किए गए पॉइंटर्स या कुछ के उपयोग से प्रबंधित किया जाता है।
नील

5

स्ट्रिंग की अपरिहार्यता का कारण भाषा में अन्य आदिम प्रकारों के साथ स्थिरता से आता है। यदि आपके पास intमान 42 है, और आप इसमें 1 मान जोड़ते हैं, तो आप 42 को नहीं बदलते हैं। आपको एक नया मान मिलता है, 43, जो आरंभिक मानों से पूरी तरह से संबंधित नहीं है। स्ट्रिंग के अलावा अन्य आदिम उत्परिवर्तन को कोई वैचारिक अर्थ नहीं देता है; और ऐसे कार्यक्रम जो स्ट्रिंग को अपरिवर्तनीय मानते हैं, अक्सर इसके बारे में तर्क करने और समझने में आसान होते हैं।

इसके अलावा, जावा वास्तव में परस्पर और अपरिवर्तनीय दोनों तार प्रदान करता है, जैसा कि आप देखते हैं StringBuilder; वास्तव में, केवल डिफ़ॉल्ट अपरिवर्तनीय स्ट्रिंग है। यदि आप StringBuilderहर जगह के आसपास के संदर्भों को पास करना चाहते हैं तो ऐसा करने के लिए आपका पूरी तरह से स्वागत है। जावा इन अवधारणाओं के लिए अलग-अलग प्रकारों ( Stringऔर StringBuilder) का उपयोग करता है क्योंकि इसमें उत्परिवर्तन या उसके प्रकार के सिस्टम में कमी को व्यक्त करने के लिए समर्थन नहीं है। जिन भाषाओं में उनके प्रकार के सिस्टम (जैसे C ++ 's const) में अपरिवर्तनीयता के लिए समर्थन है , वहाँ अक्सर एक एकल स्ट्रिंग प्रकार होता है जो दोनों उद्देश्यों को पूरा करता है।

हाँ, स्ट्रिंग होना अपरिवर्तनीय है जो किसी को अपरिवर्तनीय स्ट्रिंग्स के लिए विशिष्ट कुछ अनुकूलन को लागू करने की अनुमति देता है, जैसे कि इंटर्निंग, और थ्रेड के पार सिंक्रनाइज़ेशन के बिना स्ट्रिंग संदर्भों को पास करने की अनुमति देता है। हालांकि, यह एक सरल और सुसंगत प्रकार की प्रणाली के साथ भाषा के इच्छित लक्ष्य के साथ तंत्र को भ्रमित करता है। मैं इस बात की तुलना करता हूं कि हर कोई कचरा संग्रहण के बारे में गलत तरीके से कैसे सोचता है; कचरा संग्रह "अप्रयुक्त स्मृति का पुनर्ग्रहण" नहीं है; यह "असीमित मेमोरी वाले कंप्यूटर का अनुकरण" है । चर्चित प्रदर्शन अनुकूलन वे चीजें हैं जो अपरिवर्तनीय स्ट्रिंग्स के लक्ष्य को वास्तविक मशीनों पर अच्छा प्रदर्शन करने के लिए की जाती हैं; इस तरह के तार पहली जगह में अपरिवर्तनीय होने का कारण नहीं है।


@ बिली-ओनली .. यदि आपके पास कोई मान है जिसमें मान 42 है, और आप इसमें 1 जोड़ते हैं, तो आप 42 नहीं बदलते हैं। आपको एक नया मान मिलता है, 43, जो शुरू में पूरी तरह से असंबंधित है मान। " क्या अापको उस बारे में पूर्ण विशवास है?
शमिता वर्मा

@ शमिता: हाँ, मुझे यकीन है। 43 में 1 से 42 परिणाम जोड़ना। यह संख्या 42 का मतलब 43 की संख्या के समान नहीं है।
बिली ONeal

@ शमिता: इसी तरह, आप कुछ ऐसा नहीं कर सकते हैं 43 = 6और संख्या 43 की उम्मीद कर सकते हैं, जैसा कि संख्या 6. का मतलब है
बिली ओनेल

int i = 42; i = i + 1; यह कोड 42 मेमोरी में स्टोर करेगा और फिर उसी लोकेशन में वैल्यू को 43 में बदल देगा। इसलिए वास्तव में, वेरिएबल "i" 43 का नया मान प्राप्त करता है।
शमिता वर्मा

@ शमित: उस मामले में, आपने i42 नहीं, पर विचार किया string s = "Hello "; s += "World";। आपने वैरिएबल का मान म्यूट कर दिया s। लेकिन तार "Hello ", "World"और "Hello World"अपरिवर्तनीय हैं।
बिली ओनेल

4

अपरिवर्तनशीलता का अर्थ है कि उन वर्गों द्वारा धारण किए गए स्थिरांक जिन्हें आप संशोधित नहीं करते हैं। जिन कक्षाओं के मालिक आप नहीं हैं उनमें वे हैं जो जावा के कार्यान्वयन के मूल में हैं, और ऐसे तार जिन्हें संशोधित नहीं किया जाना चाहिए, उनमें सुरक्षा टोकन, सेवा पते, आदि जैसी चीजें शामिल हैं। आपको वास्तव में उन प्रकारों को संशोधित करने में सक्षम नहीं होना चाहिए। चीजों की (और सैंडबॉक्स मोड में काम करने पर यह दोगुना लागू होता है)।

यदि स्ट्रिंग अपरिवर्तनीय नहीं थी, तो हर बार जब आप इसे किसी ऐसे संदर्भ से पुनर्प्राप्त करते हैं, जो नहीं चाहता था कि स्ट्रिंग की सामग्री उसके पैरों के नीचे बदल जाए, तो आपको "बस मामले में" एक कॉपी लेनी होगी। जो बहुत महंगा मिलता है।


4
यह सटीक तर्क किसी भी प्रकार पर लागू होता है , केवल करने के लिए नहीं String। लेकिन, उदाहरण के लिए, Arrayएस परस्पर परिवर्तनशील हैं। तो, क्यों Stringअपरिवर्तनीय हैं और Arrayनहीं हैं। और अगर अपरिवर्तनीयता बहुत महत्वपूर्ण है, तो जावा को अपरिवर्तनीय वस्तुओं के साथ बनाने और काम करने में इतनी मुश्किल क्यों आती है?
जॉर्ग डब्ल्यू मित्तग

1
@ JörgWMittag: मेरा मानना ​​है कि मूल रूप से एक सवाल है कि वे कितने कट्टरपंथी बनना चाहते थे। एक अपरिवर्तनीय स्ट्रिंग होने पर जावा में 1.0 दिनों के लिए बहुत ही कट्टरपंथी था। भाषा के व्यापक उपयोग के लिए प्राथमिक (मुख्य रूप से या विशेष रूप से) अपरिवर्तनीय संग्रह ढांचे के होने के साथ ही बहुत अधिक कट्टरपंथी हो सकता है।
जोआचिम सॉयर

एक प्रभावी अपरिवर्तनीय संग्रह ढाँचा करना प्रदर्शन करने वाले के लिए काफी मुश्किल है, किसी ऐसे व्यक्ति के रूप में बोलना जिसने ऐसी बात लिखी है (लेकिन जावा में नहीं)। मैं यह भी पूरी तरह से इच्छा रखता हूं कि मेरे पास बहुत से सरणियाँ थीं; इससे मुझे काफी काम बचा होगा।
डोना फेलो

@ डोनल फेलो: पीसीओलेक्शंस का उद्देश्य सिर्फ इतना करना है (इसका इस्तेमाल खुद कभी नहीं किया है, हालांकि)।
जोआचिम सॉयर

3
@ JörgWMittag: ऐसे लोग (आम तौर पर विशुद्ध रूप से कार्यात्मक दृष्टिकोण से) होते हैं जो यह तर्क देते हैं कि सभी प्रकार अपरिवर्तनीय होने चाहिए। इसी तरह, मुझे लगता है कि यदि आप उन सभी मुद्दों को जोड़ते हैं जो एक समानांतर और समवर्ती सॉफ्टवेयर में परस्पर स्थिति के साथ काम करते हैं, तो आप सहमत हो सकते हैं कि अपरिवर्तनीय वस्तुओं के साथ काम करना अक्सर उत्परिवर्तित लोगों की तुलना में बहुत आसान है।
स्टीवन एवर्स

2

एक प्रणाली की कल्पना करें जहां आप कुछ डेटा स्वीकार करते हैं, इसकी शुद्धता को सत्यापित करते हैं और फिर इसे पास करते हैं (उदाहरण के लिए, एक DB में संग्रहीत किया जाता है)।

यह मानते हुए कि डेटा एक है Stringऔर इसे कम से कम 5 अक्षर लंबा होना चाहिए। आपका तरीका कुछ इस तरह दिखता है:

public void handle(String input) {
  if (input.length() < 5) {
    throw new IllegalArgumentException();
  }
  storeInDatabase(input);
}

अब हम सहमत हो सकते हैं, कि जब storeInDatabaseयहां बुलाया जाता है, inputतो आवश्यकता पूरी हो जाएगी। लेकिन अगर Stringयह परिवर्तनशील था, तो कॉल करने वाले को सत्यापित किए जाने से पहले और डेटाबेस में संग्रहीत होने से ठीक पहले , inputऑब्जेक्ट ऑब्जेक्ट को (दूसरे थ्रेड से) बदल सकता है । इसके लिए अच्छे समय की आवश्यकता होती है और यह शायद हर बार अच्छा नहीं होगा, लेकिन कभी-कभार, वह आपको डेटाबेस में अमान्य मूल्यों को संग्रहीत करने में सक्षम होगा।

अपरिवर्तनीय डेटा प्रकार इस (और बहुत से संबंधित) समस्याओं का एक बहुत ही सरल समाधान हैं: जब भी आप कुछ मूल्य की जांच करते हैं, तो आप इस तथ्य पर निर्भर कर सकते हैं कि चेक की गई स्थिति अभी भी बाद में सही है।


स्पष्टीकरण के लिए धन्यवाद। क्या होगा अगर मैं इस तरह से हैंडल विधि कहता हूं; संभाल (नया स्ट्रिंग (इनपुट + "नबरलान"))। मुझे लगता है कि मैं इस तरह db में अमान्य मान संग्रहीत कर सकता हूं।
yfklon

1
@blank: ठीक है, के बाद से inputकी handleपद्धति पहले से ही बहुत लंबा है (कोई बात नहीं क्या है मूल input है), यह केवल एक अपवाद फेंक होगा। आप विधि को कॉल करने से पहले एक नया इनपुट बना रहे हैं । वह कोई समस्या नहीं है।
जोआचिम सॉर

0

सामान्य तौर पर, आप मूल्य प्रकार और संदर्भ प्रकारों का सामना करेंगे । एक मूल्य प्रकार के साथ, आप उस वस्तु के बारे में परवाह नहीं करते हैं जो इसे दर्शाता है, आप मूल्य के बारे में परवाह करते हैं। अगर मैं आपको एक मूल्य देता हूं, तो आप उस मूल्य को वैसा ही रहने की उम्मीद करते हैं। आप इसे अचानक बदलना नहीं चाहते हैं। संख्या 5 एक मान है। आप इसे अचानक 6 में बदलने की उम्मीद नहीं करते हैं। स्ट्रिंग "हैलो" एक मूल्य है। आप इसे अचानक "पी *** बंद" में बदलने की उम्मीद नहीं करते हैं।

संदर्भ प्रकारों के साथ आप ऑब्जेक्ट के बारे में परवाह करते हैं, और आप इसे बदलने की उम्मीद करते हैं। उदाहरण के लिए, आप अक्सर किसी सरणी को बदलने की अपेक्षा करेंगे। अगर मैं आपको एक सरणी देता हूं, और आप इसे बिल्कुल वैसा ही रखना चाहते हैं, तो आपको या तो मुझ पर भरोसा करना होगा कि आप इसे न बदलें, या आप इसकी एक प्रति बना लें।

जावा स्ट्रिंग क्लास के साथ, डिजाइनरों को एक निर्णय लेना था: क्या यह बेहतर है अगर तार एक मूल्य प्रकार की तरह व्यवहार करते हैं, या उन्हें एक संदर्भ प्रकार की तरह व्यवहार करना चाहिए? जावा स्ट्रिंग्स के मामले में, निर्णय लिया गया था कि उन्हें मूल्य प्रकार होना चाहिए, जिसका अर्थ है कि चूंकि वे ऑब्जेक्ट हैं, तो उन्हें अपरिवर्तनीय ऑब्जेक्ट होना चाहिए।

विपरीत निर्णय हो सकता था, लेकिन मेरी राय में बहुत सारे सिरदर्द हो सकते हैं। जैसा कि कहीं और कहा गया है, कई भाषाओं ने एक ही निर्णय लिया और एक ही निष्कर्ष पर पहुंचे। एक अपवाद सी ++ है, जिसमें एक स्ट्रिंग वर्ग है, और तार निरंतर या गैर-स्थिर हो सकते हैं, लेकिन सी ++ में, जावा के विपरीत, ऑब्जेक्ट पैरामीटर को मान के रूप में पारित किया जा सकता है, न कि संदर्भ के रूप में।


0

मैं वास्तव में हैरान हूं कि किसी ने इस ओर इशारा नहीं किया।

उत्तर: यह आपको काफी फायदा नहीं पहुंचाएगा, भले ही यह परस्पर था। इससे आपको उतना फायदा नहीं होगा जितना कि अतिरिक्त परेशानी का कारण बनता है। आइए उत्परिवर्तन के दो सबसे सामान्य मामलों की जांच करें:

एक स्ट्रिंग के एक चरित्र को बदलना

चूँकि जावा स्ट्रिंग में प्रत्येक वर्ण 2 या 4 बाइट्स लेता है, अपने आप से पूछें कि क्या आप मौजूदा कॉपी को म्यूट कर सकते हैं?

परिदृश्य में आप 4 बाइट एक (या इसके विपरीत) के साथ एक 2 बाइट चरित्र की जगह ले रहे हैं, आपको स्ट्रिंग के शेष भाग को 2 बाइट्स को बाईं या दाईं ओर स्थानांतरित करना होगा। जो कि समग्र दृष्टिकोण से पूरी स्ट्रिंग की नकल करने से अलग नहीं है।

यह भी वास्तव में अनियमित व्यवहार है जो आम तौर पर अवांछित है। कल्पना करें कि कोई व्यक्ति अंग्रेजी पाठ के साथ किसी एप्लिकेशन का परीक्षण कर रहा है, और जब आवेदन को विदेशी देशों, जैसे चीन में अपनाया जाता है, तो पूरी बात अजीब तरीके से प्रदर्शन करने लगती है।

मौजूदा एक और स्ट्रिंग (या चरित्र) को लागू करना

यदि आपके पास दो मनमाने तार हैं, तो वे दो अलग-अलग मेमोरी स्थानों पर बैठते हैं। यदि आप दूसरे को जोड़कर पहले एक को बदलना चाहते हैं, तो आप पहले स्ट्रिंग के अंत में अतिरिक्त मेमोरी का अनुरोध नहीं कर सकते, क्योंकि यह संभवतः पहले से ही कब्जा है।

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

यदि आप कुशलता से अपीयरेंस करना चाहते हैं, तो आप उपयोग करना चाह सकते हैं StringBuilder, जो कि एक संभावित भविष्य के एपेंड के इस उद्देश्य के लिए, एक स्ट्रिंग के अंत में बहुत अधिक मात्रा में स्थान रखता है।


-2
  1. वे महंगे हैं और उन्हें अपरिवर्तनीय रखने की अनुमति देता है जैसे उप स्ट्रिंग मुख्य स्ट्रिंग के बाइट सरणी को साझा करते हैं। (गति में वृद्धि भी एक नई बाइट सरणी बनाने और अधिक कॉपी करने की आवश्यकता नहीं है)

  2. सुरक्षा - अपने पैकेज या वर्ग कोड को फिर से नाम नहीं देना चाहेंगे

    [3 पुराने हटाए गए StringBuilder src को देखा - यह स्ट्रिंग के साथ मेमोरी साझा नहीं करता है (जब तक संशोधित नहीं) मुझे लगता है कि यह 1.3 या 1.4 में था।]

  3. कैश हैशकोड

  4. परस्पर स्ट्रिंग्स के लिए SB (बिल्डर या बफर आवश्यकतानुसार) का उपयोग करें


2
1. बेशक, ऐसा होने पर स्ट्रिंग के बड़े हिस्सों को नष्ट करने में सक्षम नहीं होने का दंड है। इंटर्निंग मुफ्त नहीं है; हालांकि यह कई वास्तविक विश्व कार्यक्रमों के लिए प्रदर्शन में सुधार करता है। 2. आसानी से "स्ट्रिंग" और "इम्यूटेबल स्ट्रिंग" हो सकते हैं जो उस आवश्यकता को पूरा कर सकते हैं। 3. मुझे यकीन नहीं है कि मैं ...
बिली ओनेल

.3। हैश कोड कैश होना चाहिए था। यह भी एक उत्परिवर्ती स्ट्रिंग के साथ किया जा सकता है। @ billy-oneal
tgkprog

-4

जावा में स्ट्रिंग्स एक आदिम डेटा प्रकार होना चाहिए। यदि वे होते, तो तार परस्पर परिवर्तित होने में डिफ़ॉल्ट होते और अंतिम कीवर्ड अपरिवर्तनीय तार उत्पन्न करते। म्यूटेबल स्ट्रिंग उपयोगी होते हैं और इसलिए स्ट्रैबफ़र, स्ट्रिंगबिल्डर और चार्टेड कक्षाओं में म्यूटेबल स्ट्रिंग्स के लिए कई हैक होते हैं।


3
यह "क्यों" का जवाब नहीं देता है कि अब यह क्या है जो सवाल पूछता है। इसके अतिरिक्त, जावा फाइनल उस तरह से काम नहीं करता है। म्यूटेबल स्ट्रिंग्स हैक नहीं होती हैं, बल्कि स्ट्रिंग्स के सबसे सामान्य उपयोगों पर आधारित वास्तविक डिजाइन विचार और अनुकूलन जो jvm को बेहतर बनाने के लिए किए जा सकते हैं।

1
"क्यों" का उत्तर एक खराब भाषा डिजाइन निर्णय है। म्यूटेबल स्ट्रिंग्स का समर्थन करने के तीन अलग-अलग तरीके एक हैक है जिसे कंपाइलर / जेवीएम को संभालना चाहिए।
CWallach

3
स्ट्रिंग और स्ट्रिंगरफ़र मूल थे। StringBuilder को बाद में StringBuffer के साथ एक डिज़ाइन कठिनाई को पहचानते हुए जोड़ा गया था। विभिन्न वस्तुओं के अलग-अलग होने के कारण उत्परिवर्तनीय और अपरिवर्तनीय तार कई भाषाओं में पाए जाते हैं क्योंकि डिजाइन पर विचार बार-बार किया गया और निर्णय लिया गया कि प्रत्येक बार अलग-अलग वस्तुएं हैं। सी # "स्ट्रिंग्स अपरिवर्तनीय हैं" और क्यों .NET स्ट्रिंग अपरिवर्तनीय है? , उद्देश्य C NSString अपरिवर्तनीय है जबकि NSMutableString परस्पर है। stackoverflow.com/questions/9544182
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.