जावा और .NET में तार क्यों नहीं बदले जा सकते हैं?


190

ऐसा क्यों है कि उन्होंने Stringजावा और .NET (और कुछ अन्य भाषाओं) में अपरिवर्तनीय बनाने का फैसला किया है ? उन्होंने इसे परस्पर क्यों नहीं बनाया?


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

8
शुक्रिया बेलुगाब, लेकिन मैं उसका नहीं हूं, मैं उसका हूं। जाहिर तौर पर लोग सांस्कृतिक मतभेदों पर विचार नहीं करते हैं।
chrissie1

7
मेरी क्षमायाचना - chrissie ब्रिटेन में एक लड़की का नाम है (आम तौर पर) - मुझे एक और सांस्कृतिक अंतर का शिकार बनाने के लिए :-)
belugabob

बस एक नोट, .NET Stringमें वास्तव में आंतरिक रूप से परिवर्तनशील है। StringBuilder.NET 2.0 में स्ट्रिंग को म्यूट करता है । मैं इसे यहाँ छोड़ दूँगा।
एल्विन वोंग

दरअसल नेट तार कर रहे हैं परिवर्तनशील। और यह एक हैक की बिल्ली भी नहीं है।
Bitterblue

जवाबों:


204

प्रभावी जावा के अनुसार , अध्याय 4, पृष्ठ 73, 2 संस्करण:

"इसके कई अच्छे कारण हैं: अपरिवर्तनीय कक्षाएं म्यूट करने योग्य कक्षाओं की तुलना में डिजाइन, कार्यान्वयन और उपयोग करना आसान हैं। वे त्रुटि के लिए कम प्रवण हैं और अधिक सुरक्षित हैं।

[...]

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

[...]

अपरिवर्तनीय वस्तुएं स्वाभाविक रूप से धागा-सुरक्षित हैं; उन्हें कोई सिंक्रनाइज़ेशन की आवश्यकता नहीं है। उन्हें समवर्ती रूप से एक्सेस करने वाले कई थ्रेड्स द्वारा दूषित नहीं किया जा सकता है। यह थ्रेड सुरक्षा को प्राप्त करने के लिए सबसे आसान तरीका है। वास्तव में, कोई भी धागा किसी अन्य धागे के अपरिवर्तनीय वस्तु पर कभी भी प्रभाव नहीं डाल सकता है। इसलिए, अपरिवर्तनीय वस्तुओं को स्वतंत्र रूप से साझा किया जा सकता है

[...]

इसी अध्याय के अन्य छोटे बिंदु:

न केवल आप अपरिवर्तनीय वस्तुओं को साझा कर सकते हैं, लेकिन आप उनके इंटर्न को साझा कर सकते हैं।

[...]

अपरिवर्तनीय वस्तुएं अन्य वस्तुओं के लिए महान बिल्डिंग ब्लॉक बनाती हैं, चाहे वह उत्परिवर्तनीय या अपरिवर्तनीय हो।

[...]

अपरिवर्तनीय वर्गों का एकमात्र वास्तविक नुकसान यह है कि उन्हें प्रत्येक अलग मूल्य के लिए एक अलग वस्तु की आवश्यकता होती है।


22
मेरे उत्तर के दूसरे वाक्य को पढ़ें: अपरिवर्तनीय वर्ग, उत्परिवर्तित कक्षाओं की तुलना में डिजाइन, कार्यान्वयन और उपयोग में आसान होते हैं। वे त्रुटि के लिए कम प्रवण हैं और अधिक सुरक्षित हैं।
प्रिसेंस फ्लोफ

5
@PRINCESSFLUFF मैं यह जोड़ना चाहूंगा कि एक ही थ्रेड पर साझा करने योग्य म्यूटेशन खतरनाक है। उदाहरण के लिए, एक रिपोर्ट की प्रतिलिपि बनाना report2.Text = report1.Text;:। फिर, कहीं और, पाठ को संशोधित करना report2.Text.Replace(someWord, someOtherWord);:। यह पहली रिपोर्ट के साथ-साथ दूसरी भी बदलेगी।
13

10
@ सैम ने यह नहीं पूछा कि "वे उत्परिवर्तित क्यों नहीं हो सकते हैं", उन्होंने पूछा "क्यों उन्होंने अपरिवर्तनीय बनाने का फैसला किया" जो यह पूरी तरह से उत्तर देता है।
जेम्स

1
@PRINCESSFLUFF यह उत्तर विशेष रूप से स्ट्रिंग्स को संबोधित नहीं करता है। वह ओपी सवाल था। यह बहुत निराशाजनक है - यह एसओ पर हर समय होता है और स्ट्रिंग अपरिवर्तनीय प्रश्नों के साथ भी। यहां जवाब अपरिवर्तनीयता के सामान्य लाभों के बारे में बात करता है। तो फिर सभी प्रकार अपरिवर्तनीय क्यों नहीं हैं? क्या आप कृपया वापस जा सकते हैं और स्ट्रिंग को संबोधित कर सकते हैं?
होवीकैंप

@ ह्वीकैम्प मुझे लगता है कि इसका उत्तर इस तथ्य से मिलता है कि तार परस्पर परिवर्तित हो सकते थे (कुछ भी नहीं है कि एक काल्पनिक उत्परिवर्ती स्ट्रिंग वर्ग को मौजूदा से रोकता है)। उन्होंने सिर्फ सादगी के लिए ऐसा नहीं करने का फैसला किया, और क्योंकि यह उपयोग के 99% मामलों को कवर करता है। वे अभी भी अन्य 1% मामलों के लिए स्ट्रिंगबर्ल प्रदान करते हैं।
डैनियल गार्सिया रुबियो

102

कम से कम दो कारण हैं।

पहला - सुरक्षा http://www.javafaq.nu/java-article1060.html

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

दूसरा - मेमोरी दक्षता http://hikrish.blogspot.com/2006/07/why-string-class-is-immigable.html

JVM आंतरिक रूप से "स्ट्रिंग पूल" को बनाए रखता है। मेमोरी दक्षता प्राप्त करने के लिए, JVM पूल से स्ट्रिंग ऑब्जेक्ट को संदर्भित करेगा। यह नई स्ट्रिंग ऑब्जेक्ट्स नहीं बनाएगा। इसलिए, जब भी आप एक नया स्ट्रिंग शाब्दिक बनाते हैं, तो JVM पूल में जांच करेगा कि यह पहले से मौजूद है या नहीं। यदि पहले से ही पूल में मौजूद हैं, तो बस उसी ऑब्जेक्ट का संदर्भ दें या पूल में नई ऑब्जेक्ट बनाएं। समान स्ट्रिंग ऑब्जेक्ट्स के लिए कई संदर्भ बिंदु होंगे, यदि कोई मान बदलता है, तो यह सभी संदर्भों को प्रभावित करेगा। इसलिए, सूरज ने इसे अपरिवर्तनीय बनाने का फैसला किया।


यदि आप String.intern () का उपयोग करते हैं, तो पुन: उपयोग के बारे में यह एक अच्छा बिंदु है, और विशेष रूप से सच है। सभी तारों को अपरिवर्तनीय बनाए बिना पुन: उपयोग करना संभव होगा, लेकिन जीवन उस बिंदु पर जटिल हो जाता है।
jsight

3
इस दिन और उम्र में न तो उनमें से कोई मुझे बहुत वैध कारण लगता है।
ब्रायन नोब्लुच

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

7
सुरक्षा वास्तव में अपरिवर्तनीय स्ट्रिंग्स के लिए रायसन डी'अत्रे का हिस्सा नहीं है - आपका ओएस कर्नेल-मोड बफ़र्स के लिए स्ट्रिंग्स की प्रतिलिपि बनाएगा और समय-समय पर हमलों से बचने के लिए वहां पहुंच-जांच करेगा। यह वास्तव में थ्रेड सुरक्षा और प्रदर्शन के बारे में है :)
snemarch

1
मेमोरी दक्षता तर्क भी काम नहीं करता है। सी की तरह एक मूल भाषा में, स्ट्रिंग स्थिरांक प्रारंभिक डेटा अनुभाग में डेटा के लिए केवल संकेत हैं - वे वैसे भी केवल पढ़ने योग्य / अपरिवर्तनीय हैं। "अगर कोई मान बदलता है" - फिर से, पूल से तार वैसे भी पढ़े जाते हैं।
wj32

57

वास्तव में, कारण स्ट्रिंग जावा में अपरिवर्तनीय हैं सुरक्षा के साथ बहुत कुछ नहीं करना है। दो मुख्य कारण निम्नलिखित हैं:

थ्रेड सुरक्षा:

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

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

प्रदर्शन:

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

इससे भी महत्वपूर्ण बात, अपरिवर्तनीय तार उन्हें अपने आंतरिक डेटा को साझा करने की अनुमति देते हैं। कई स्ट्रिंग ऑपरेशन के लिए, इसका मतलब है कि वर्णों के अंतर्निहित सरणी को कॉपी करने की आवश्यकता नहीं है। उदाहरण के लिए, मान लें कि आप स्ट्रिंग के पहले पाँच अक्षर लेना चाहते हैं। जावा में, आप myString.substring (0,5) को कॉल करेंगे। इस स्थिति में, विकल्प () विधि क्या करती है, बस एक नई स्ट्रिंग ऑब्जेक्ट बनाने के लिए जो myString के अंतर्निहित char को साझा करता है [लेकिन यह कौन जानता है कि यह अनुक्रमणिका 0 से शुरू होता है और उस char के सूचकांक 5 पर समाप्त होता है]। इसे ग्राफिकल रूप में रखने के लिए, आप निम्नलिखित बातों को पूरा करेंगे:

 |               myString                  |
 v                                         v
"The quick brown fox jumps over the lazy dog"   <-- shared char[]
 ^   ^
 |   |  myString.substring(0,5)

यह इस तरह के ऑपरेशन को बहुत सस्ता बनाता है, और ओ (1) क्योंकि ऑपरेशन न तो मूल स्ट्रिंग की लंबाई पर निर्भर करता है, और न ही प्रतिस्थापन की लंबाई पर हमें निकालने की आवश्यकता होती है। इस व्यवहार के कुछ स्मृति लाभ भी हैं, क्योंकि कई तार अपने अंतर्निहित चार [] साझा कर सकते हैं।


6
सब्सिडिंग्स को उन संदर्भों के रूप में लागू करना जो अंतर्निहित साझा करते हैं, char[]बल्कि एक संदिग्ध प्रश्न निर्णय है। यदि आप पूरी फाइल को एक स्ट्रिंग में पढ़ते हैं और केवल 1-कैरेक्टर विकल्प के संदर्भ को बनाए रखते हैं, तो पूरी फाइल को मेमोरी में रखना होगा।
गाबे

5
वास्तव में, मैं एक वेबसाइट क्रॉलर बनाते समय उस विशेष गोच में भाग गया, जिसे केवल पूरे पृष्ठ से कुछ शब्द निकालने की आवश्यकता थी। संपूर्ण पृष्ठ HTML कोड मेमोरी में था, और चार [] को साझा करने के कारण, मैंने पूरे HTML कोड को रखा, हालांकि मुझे केवल कुछ बाइट्स की आवश्यकता थी। इसके लिए एक वैकल्पिक हल नया स्ट्रिंग (मूल। Substring (.., ..)) का उपयोग करना है, स्ट्रिंग (स्ट्रिंग) निर्माता अंतर्निहित सरणी की प्रासंगिक श्रेणी की एक प्रतिलिपि बनाता है।
लॉर्डऑफइंप्ल्स

1
बाद के बदलावों को शामिल करने के लिए एक परिशिष्ट: String.substring()ऊपर से टिप्पणियों में वर्णित समस्याओं को रोकने के लिए , जेवी 7 के बाद से, एक पूर्ण प्रतिलिपि करता है। जावा 8 में, char[]साझाकरण को सक्षम करने वाले दो फ़ील्ड , countऔर offset, हटा दिए जाते हैं, इस प्रकार स्ट्रिंग इंस्टेंस की मेमोरी फ़ुटप्रिंट को कम करते हैं।
क्रिश्चियन सेमरू

मैं थीड सेफ्टी पार्ट से सहमत हूं, लेकिन सस्पेंशन केस से जुड़ा है।
Gqqnbig

@LoveRight: इसके बाद java.lang.String ( grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/… ) के सोर्स कोड की जांच करें , यह जावा 6 तक सभी तरह से है (जो कि) वर्तमान था जब यह उत्तर लिखा गया था)। मैं जाहिरा तौर पर जावा 7 में बदल गया हूं
लॉर्डऑफइग्स

28

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


11

एक को वास्तव में पूछना चाहिए, "एक्स को परस्पर क्यों होना चाहिए?" राजकुमारी फ़्लफ़ द्वारा पहले से बताए गए लाभों के कारण, अपरिवर्तनीयता के लिए डिफ़ॉल्ट रूप से बेहतर है । यह एक अपवाद होना चाहिए कि कुछ परिवर्तनशील है।

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


7

वाह! मैं यहाँ गलत सूचना पर विश्वास नहीं कर सकता। Stringअपरिवर्तनीय होना सुरक्षा के साथ कुछ भी नहीं है। यदि किसी के पास पहले से ही चल रहे एप्लिकेशन में ऑब्जेक्ट्स तक पहुंच है (जिसे यह मानना ​​होगा कि यदि आप Stringअपने ऐप में किसी 'हैकिंग' के खिलाफ पहरा देने की कोशिश कर रहे हैं ), तो वे निश्चित रूप से हैकिंग के लिए उपलब्ध अन्य अवसरों के बहुत सारे होंगे।

यह एक काफी उपन्यास विचार है कि Stringथ्रेडिंग मुद्दों को संबोधित करने की अपरिहार्यता है। हम्मम ... मेरे पास एक ऐसी वस्तु है जिसे दो अलग-अलग धागों से बदला जा रहा है। मैं इसका कैसे समाधान करूं? ऑब्जेक्ट तक पहुंच को सिंक्रनाइज़ करें? Naawww ... चलो किसी को भी वस्तु को बिल्कुल भी बदलने न दें - जो हमारे सभी गंदे समसामयिक मुद्दों को ठीक कर देगा! वास्तव में, चलो सभी वस्तुओं को अपरिवर्तनीय बनाते हैं, और फिर हम जावा भाषा से सिंक्रोनाइज्ड संदूषण को हटा सकते हैं।

वास्तविक कारण (ऊपर दूसरों द्वारा इंगित) मेमोरी ऑप्टिमाइज़ेशन है। एक ही स्ट्रिंग शाब्दिक के लिए किसी भी एप्लिकेशन में बार-बार उपयोग किया जाना काफी आम है। यह बहुत आम है, वास्तव में, दशकों पहले, कई संकलक ने केवल एक Stringशाब्दिक रूप से एक ही उदाहरण के भंडारण का अनुकूलन किया था । इस अनुकूलन का दोष रनटाइम कोड है जो Stringशाब्दिक रूप से संशोधित करता है एक समस्या का परिचय देता है क्योंकि यह अन्य सभी कोड के लिए उदाहरण को संशोधित करता है जो इसे साझा करता है। उदाहरण के लिए, Stringशाब्दिक "dog"को बदलने के लिए एक आवेदन में कहीं भी एक फ़ंक्शन के लिए अच्छा नहीं होगा "cat"। ए के printf("dog")परिणामस्वरूप परिणाम "cat"लिखा जाएगा । उस कारण से, कोड के खिलाफ रखवाली का एक तरीका होना चाहिए जो बदलने का प्रयास करता हैStringशाब्दिक (यानी, उन्हें अपरिवर्तनीय बनाते हैं)। कुछ संकलक (OS से समर्थन के साथ) Stringशाब्दिक रूप से एक विशेष मेमोरी मेमोरी सेगमेंट में इसे पूरा करेंगे, जो कि यदि एक लिखित प्रयास किया गया था, तो मेमोरी फ़ॉल्ट का कारण होगा।

जावा में इसे इंटर्निंग के रूप में जाना जाता है। यहाँ जावा कंपाइलर दशकों से कंपाइलरों द्वारा किए गए एक मानक मेमोरी ऑप्टिमाइज़ेशन का अनुसरण कर रहा है। और इन Stringशाब्दिकों के एक ही मुद्दे को संबोधित करने के लिए रनटाइम में संशोधित किया गया, जावा बस Stringवर्ग को अपरिवर्तनीय बनाता है (i। ई, आपको कोई बसने वाला नहीं देता है जो आपको Stringसामग्री को बदलने की अनुमति देगा )। Stringअगर Stringशाब्दिक अर्थों में इंटर्नशिप नहीं होती है, तो अपरिवर्तनीय नहीं होगा ।


3
अपरिवर्तनीयता और थ्रेडिंग टिप्पणी के बारे में मैं बहुत असहमत हूं, ऐसा लगता है कि आपको वहां बात नहीं मिल रही है। और अगर जावा कार्यान्वयनकर्ताओं में से एक जोश बलोच का कहना है कि यह डिजाइन के मुद्दों में से एक था, तो यह गलत सूचना कैसे हो सकती है?
२।

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

5
@जिम: मेमोरी ऑप्टिमाइजेशन 'द' कारण नहीं है, यह 'ए' कारण है। थ्रेड-सेफ्टी भी 'ए' कारण है, क्योंकि अपरिवर्तनीय वस्तुएं स्वाभाविक रूप से थ्रेड-सेफ होती हैं और डेविड द्वारा बताए गए अनुसार किसी भी महंगी सिंक्रनाइज़ेशन की आवश्यकता नहीं होती है। थ्रेड सुरक्षा वास्तव में किसी वस्तु के अपरिवर्तनीय होने का एक साइड-इफेक्ट है। आप ऑब्जेक्ट को "अस्थायी रूप से" अपरिवर्तनीय बनाने के तरीके के रूप में सिंक्रनाइज़ेशन के बारे में सोच सकते हैं (ReaderWriterLock इसे केवल पढ़ने के लिए बनाएगा, और एक नियमित लॉक इसे पूरी तरह से दुर्गम बना देगा, जो निश्चित रूप से इसे अच्छी तरह से अपरिवर्तनीय बनाता है)।
त्रिनको

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

7

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

एक मूल्य कुछ ऐसा है जिस पर आप भरोसा कर सकते हैं कि आपकी पीठ के पीछे कोई बदलाव नहीं होगा। यदि आप लिखते हैं: String str = someExpr(); आप इसे तब तक बदलना नहीं चाहते जब तक आप कुछ नहीं करते str

Stringके रूप में एक Objectस्वाभाविक रूप से सूचक शब्दार्थ है, मूल्य शब्दार्थ प्राप्त करने के साथ ही यह अपरिवर्तनीय होने की आवश्यकता है।


7

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


6

मुझे पता है कि यह एक टक्कर है, लेकिन ... क्या वे वास्तव में अपरिवर्तनीय हैं? निम्नलिखित को धयान मे रखते हुए।

public static unsafe void MutableReplaceIndex(string s, char c, int i)
{
    fixed (char* ptr = s)
    {
        *((char*)(ptr + i)) = c;
    }
}

...

string s = "abc";
MutableReplaceIndex(s, '1', 0);
MutableReplaceIndex(s, '2', 1);
MutableReplaceIndex(s, '3', 2);
Console.WriteLine(s); // Prints 1 2 3

आप इसे एक विस्तार विधि भी बना सकते हैं।

public static class Extensions
{
    public static unsafe void MutableReplaceIndex(this string s, char c, int i)
    {
        fixed (char* ptr = s)
        {
            *((char*)(ptr + i)) = c;
        }
    }
}

जो निम्नलिखित कार्य करता है

s.MutableReplaceIndex('1', 0);
s.MutableReplaceIndex('2', 1);
s.MutableReplaceIndex('3', 2);

निष्कर्ष: वे एक अपरिवर्तनीय स्थिति में हैं जो संकलक द्वारा जाना जाता है। Couse के ऊपर केवल .NET स्ट्रिंग्स पर लागू होता है क्योंकि जावा में पॉइंटर्स नहीं हैं। हालाँकि C # में पॉइंटर्स का उपयोग करके एक स्ट्रिंग को पूरी तरह से म्यूट किया जा सकता है। ऐसा नहीं है कि पॉइंटर्स का उपयोग कैसे किया जाता है, इसका व्यावहारिक उपयोग होता है या सुरक्षित रूप से उपयोग किया जाता है; हालांकि यह संभव है, इस प्रकार पूरे "उत्परिवर्तित" नियम को झुकाते हुए। आप आम तौर पर सीधे एक स्ट्रिंग के सूचकांक को संशोधित नहीं कर सकते हैं और यह एकमात्र तरीका है। एक तरीका है कि इसे स्ट्रिंग के पॉइंटर इंस्टेंस को रोकने या प्रतिलिपि बनाने से रोका जा सकता है जब एक स्ट्रिंग को इंगित किया जाता है, लेकिन न तो किया जाता है, जो सी # में तार पूरी तरह से अपरिवर्तनीय नहीं बनाता है।


1
+1। .NET स्ट्रिंग्स वास्तव में अपरिवर्तनीय नहीं हैं; वास्तव में, यह हर समय स्ट्रिंग और स्टर्लिंगब्यूअल कक्षाओं में सही कारणों के लिए किया जाता है।
जेम्स को

3

अधिकांश उद्देश्यों के लिए, एक "स्ट्रिंग" एक सार्थक परमाणु इकाई है, जिसका उपयोग एक संख्या की तरह ही किया जाता है

यह पूछना कि एक स्ट्रिंग के अलग-अलग वर्ण परस्पर क्यों नहीं हैं, इसलिए यह पूछना पसंद है कि किसी पूर्णांक के अलग-अलग बिट्स परस्पर क्यों नहीं हैं।

आपको पता होना चाहिए कि क्यों। बस इसके बारे में सोचो।

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

हम "स्ट्रिंग्स" के साथ गणना करते हैं और तुलना करते हैं कि हम संख्याओं के साथ कैसे करते हैं। यदि तार (या पूर्णांक) परस्पर परिवर्तित होते हैं, तो हमें किसी भी प्रकार की गणना को मज़बूती से करने के लिए उनके मूल्यों को अपरिवर्तनीय स्थानीय रूपों में लॉक करने के लिए विशेष कोड लिखना होगा। इसलिए, एक संख्यात्मक पहचानकर्ता की तरह एक स्ट्रिंग के बारे में सोचना सबसे अच्छा है, लेकिन 16, 32 या 64 बिट लंबा होने के बजाय, यह सैकड़ों बिट लंबा हो सकता है।

जब कोई "स्ट्रिंग" कहता है, तो हम सभी अलग-अलग चीजों के बारे में सोचते हैं। जो लोग इसे केवल पात्रों के एक सेट के रूप में सोचते हैं, किसी विशेष उद्देश्य को ध्यान में रखते हुए, निश्चित रूप से यह याद किया जाएगा कि किसी ने सिर्फ यह तय किया कि उन्हें उन पात्रों में हेरफेर करने में सक्षम नहीं होना चाहिए। लेकिन "स्ट्रिंग" वर्ग केवल वर्णों की एक सरणी नहीं है। यह एक है STRING, नहीं है char[]। अवधारणा के बारे में कुछ बुनियादी धारणाएं हैं जिन्हें हम "स्ट्रिंग" के रूप में संदर्भित करते हैं, और इसे आम तौर पर एक संख्या की तरह कोडित डेटा के सार्थक, परमाणु इकाई के रूप में वर्णित किया जा सकता है। जब लोग "हेरफेर स्ट्रिंग्स" के बारे में बात करते हैं, तो शायद वे स्ट्रिंग्स बनाने के लिए हेरफेर करने वाले पात्रों के बारे में बात कर रहे हैं , और एक स्ट्रिंगबर्ल उसके लिए बहुत अच्छा है।

एक पल के लिए विचार करें कि यह क्या होगा यदि तार परस्पर भिन्न थे। निम्न API फ़ंक्शन को किसी भिन्न उपयोगकर्ता के लिए लौटने की जानकारी में धोखा दिया जा सकता है यदि उत्परिवर्तनीय उपयोगकर्ता नाम स्ट्रिंग जानबूझकर या अनजाने में किसी अन्य थ्रेड द्वारा संशोधित किया जाता है, जबकि यह फ़ंक्शन इसका उपयोग कर रहा है:

string GetPersonalInfo( string username, string password )
{
    string stored_password = DBQuery.GetPasswordFor( username );
    if (password == stored_password)
    {
        //another thread modifies the mutable 'username' string
        return DBQuery.GetPersonalInfoFor( username );
    }
}

सुरक्षा केवल 'अभिगम नियंत्रण' के बारे में नहीं है, यह 'सुरक्षा' और 'शुद्धता की गारंटी' के बारे में भी है। यदि कोई विधि आसानी से नहीं लिखी जा सकती है और एक साधारण गणना या तुलनात्मक रूप से तुलना करने के लिए निर्भर है, तो इसे कॉल करना सुरक्षित नहीं है, लेकिन प्रोग्रामिंग भाषा में ही कॉल करना सुरक्षित होगा।


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

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

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

1
'ऑब्जेक्ट-ओरिएंटेड कोड में संकेत स्वाभाविक रूप से असुरक्षित होते हैं' जब तक कि आप उन्हें संदर्भ नहीं कहते । जावा में संदर्भ C ++ में इंगित करने के लिए अलग नहीं हैं (केवल सूचक अंकगणित अक्षम है)। एक अलग अवधारणा स्मृति प्रबंधन है जिसे प्रबंधित या मैन्युअल किया जा सकता है, लेकिन यह एक अलग बात है। आप जीसी होने के बिना संदर्भ शब्दार्थ (बिना अंकगणित वाले पॉइंटर्स) रख सकते थे (विपरीत इस अर्थ में कठिन होगा कि अभिकर्मक का शब्दार्थ साफ करना कठिन होगा, लेकिन अक्षम्य नहीं)
डेविड रोड्रिग्ज़ - ड्रिबीज़

दूसरी बात यह है कि अगर तार लगभग अपरिवर्तनीय हैं, लेकिन ऐसा नहीं है, (मुझे यहां पर्याप्त सीएलआई नहीं पता है), जो सुरक्षा कारणों से वास्तव में खराब हो सकता है। कुछ पुराने जावा कार्यान्वयन में आप ऐसा कर सकते हैं, और मुझे कोड का एक स्निपेट मिला, जो उस स्ट्रिंग्स को आंतरिक बनाने के लिए उपयोग किया गया था (अन्य आंतरिक स्ट्रिंग का पता लगाने का प्रयास करें, जिसका मान समान है, पॉइंटर साझा करें, पुराने मेमोरी ब्लॉक को हटा दें) और पिछले दरवाजे का उपयोग करें एक अलग कक्षा में एक गलत व्यवहार के लिए स्ट्रिंग सामग्री को फिर से लिखना। ("DELETE" के लिए "SELECT *" पुनर्लेखन पर विचार करें)
डेविड रॉड्रिग्ज - dribeas

3

सुरक्षा के लिए अपरिवर्तनीयता इतनी निकटता से जुड़ी नहीं है। उसके लिए, कम से कम .NET में, आपको SecureStringक्लास मिलती है ।

बाद में संपादित करें: जावा में आपको GuardedStringएक समान कार्यान्वयन मिलेगा ।


2

C ++ में स्ट्रिंग उत्परिवर्तन का निर्णय बहुत सारी समस्याओं का कारण बनता है, इस उत्कृष्ट लेख केल्विन हेनी ने मैड गाय रोग के बारे में देखा ।

गाय = कॉपी पर लिखें।


2

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

नकारात्मक पक्ष यह है कि समवर्ती बहुत सारे अतिरिक्त Stringहैं जो केवल संक्रमणकालीन हैं और केवल कचरा बन जाते हैं, वास्तव में स्मृति प्रदर्शन को नुकसान पहुंचाते हैं। आपके पास StringBufferऔर StringBuilder(जावा में, StringBuilderइन मामलों में स्मृति बनाए रखने के लिए उपयोग करने के लिए नेट में भी है)।


1
ध्यान रखें कि "स्ट्रिंग पूल" का उपयोग स्वचालित रूप से सभी स्ट्रिंग्स के लिए नहीं किया जाता है जब तक कि आप स्पष्ट रूप से "इंटर ()" एड स्ट्रिंग्स का उपयोग नहीं करते हैं।
jsight

2

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


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

वास्तव में मेरा मानना ​​है कि आप किसी भी अपरिवर्तनीय वस्तु को परावर्तन द्वारा बदल सकते हैं।
Gqqnbig

0

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


0

लगभग हर नियम के लिए एक अपवाद है:

using System;
using System.Runtime.InteropServices;

namespace Guess
{
    class Program
    {
        static void Main(string[] args)
        {
            const string str = "ABC";

            Console.WriteLine(str);
            Console.WriteLine(str.GetHashCode());

            var handle = GCHandle.Alloc(str, GCHandleType.Pinned);

            try
            {
                Marshal.WriteInt16(handle.AddrOfPinnedObject(), 4, 'Z');

                Console.WriteLine(str);
                Console.WriteLine(str.GetHashCode());
            }
            finally
            {
                handle.Free();
            }
        }
    }
}

-1

यह काफी हद तक सुरक्षा कारणों से है। यदि आप भरोसा नहीं कर सकते हैं कि आपका Stringटेम्परप्रूफ है, तो सिस्टम को सुरक्षित करना बहुत कठिन है ।


1
क्या आप "टैम्परप्रूफ" से क्या मतलब है, इसका एक उदाहरण दे सकते हैं। यह जवाब वास्तव में संदर्भ से बाहर लगता है।
गेरेली ओरोस
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.