संगामिति
जावा को शुरू से ही संगामिति के विचारों से परिभाषित किया गया था। जैसा कि अक्सर उल्लेख किया गया है कि साझा उत्परिवर्ती समस्याग्रस्त हैं। एक चीज दूसरे धागे की पीठ के पीछे एक और परिवर्तन कर सकती है बिना उस धागे के बारे में पता चलेगा।
मल्टीथ्रेडेड 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 ने डिफ़ॉल्ट स्ट्रिंग के एक ही डिज़ाइन विकल्प को अपरिवर्तनीय बनाया।
लुआ के तार भी अपरिवर्तनीय हैं।
साथ ही अजगर ।
ऐतिहासिक रूप से, लिस्प, स्कीम, स्मॉलटॉक सभी ने स्ट्रिंग को इंटर्न किया है और इस तरह यह अपरिवर्तनीय है। अधिक आधुनिक गतिशील भाषाएं अक्सर किसी न किसी तरह से तार का उपयोग करती हैं जिसके लिए उन्हें अपरिवर्तनीय होना चाहिए (यह एक स्ट्रिंग नहीं हो सकता है , लेकिन यह अपरिवर्तनीय है)।
निष्कर्ष
ये डिज़ाइन विचार बार-बार भाषाओं की भीड़ में बनाए गए हैं। यह आम सहमति है कि अपरिवर्तनीय तार, उनके सभी अजीबता के लिए, विकल्पों से बेहतर हैं और बेहतर कोड (कम बग) और तेजी से निष्पादन योग्य समग्र करने के लिए नेतृत्व करते हैं।