एक अपरिवर्तनीय वस्तु एक ऐसी वस्तु है जहां आंतरिक क्षेत्र (या कम से कम, सभी आंतरिक क्षेत्र जो इसके बाहरी व्यवहार को प्रभावित करते हैं) को बदला नहीं जा सकता है।
अपरिवर्तनीय स्ट्रिंग्स के बहुत सारे फायदे हैं:
प्रदर्शन: निम्नलिखित कार्रवाई करें:
String substring = fullstring.substring(x,y);
प्रतिस्थापन () विधि के लिए अंतर्निहित सी शायद कुछ इस तरह है:
// Assume string is stored like this:
struct String { char* characters; unsigned int length; };
// Passing pointers because Java is pass-by-reference
struct String* substring(struct String* in, unsigned int begin, unsigned int end)
{
struct String* out = malloc(sizeof(struct String));
out->characters = in->characters + begin;
out->length = end - begin;
return out;
}
ध्यान दें कि किसी भी वर्ण को कॉपी नहीं करना है! यदि स्ट्रिंग ऑब्जेक्ट उत्परिवर्तनीय था (वर्ण बाद में बदल सकते हैं) तो आपको सभी वर्णों को कॉपी करना होगा, अन्यथा प्रतिस्थापन में वर्णों में परिवर्तन बाद में अन्य स्ट्रिंग में परिलक्षित होगा।
Concurrency: यदि किसी अपरिवर्तनीय वस्तु की आंतरिक संरचना वैध है, तो यह हमेशा मान्य होगी। इस बात की कोई संभावना नहीं है कि विभिन्न धागे उस वस्तु के भीतर एक अमान्य स्थिति बना सकते हैं। इसलिए, अपरिवर्तनीय वस्तुएं थ्रेड सेफ हैं ।
कचरा संग्रह: कचरा संग्रहकर्ता के लिए अपरिवर्तनीय वस्तुओं के बारे में तार्किक निर्णय लेना बहुत आसान है।
हालांकि, वहाँ भी अपरिवर्तनीयता के लिए नीचे हैं:
प्रदर्शन: रुको, मैंने सोचा था कि आपने कहा कि प्रदर्शन अपरिवर्तनीयता का एक उल्टा था! खैर, यह कभी-कभी होता है, लेकिन हमेशा नहीं। निम्नलिखित कोड लें:
foo = foo.substring(0,4) + "a" + foo.substring(5); // foo is a String
bar.replace(4,5,"a"); // bar is a StringBuilder
दो पंक्तियाँ दोनों चौथे वर्ण को "a" अक्षर से बदल देती हैं। न केवल कोड का दूसरा टुकड़ा अधिक पठनीय है, यह तेज है। देखो कि आपको फू के लिए अंतर्निहित कोड कैसे करना होगा। सबस्ट्रिंग आसान हैं, लेकिन अब क्योंकि पहले से ही अंतरिक्ष में एक चरित्र पांच है और कुछ और हो सकता है कि आप फू को संदर्भित कर सकें, आप इसे बदल नहीं सकते; आपको पूरी स्ट्रिंग की प्रतिलिपि बनानी होगी (बेशक इस कार्यक्षमता में से कुछ वास्तविक अंतर्निहित C में फ़ंक्शन में सार है, लेकिन यहां बिंदु उस कोड को दिखाना है जो सभी को एक ही स्थान पर निष्पादित करता है)।
struct String* concatenate(struct String* first, struct String* second)
{
struct String* new = malloc(sizeof(struct String));
new->length = first->length + second->length;
new->characters = malloc(new->length);
int i;
for(i = 0; i < first->length; i++)
new->characters[i] = first->characters[i];
for(; i - first->length < second->length; i++)
new->characters[i] = second->characters[i - first->length];
return new;
}
// The code that executes
struct String* astring;
char a = 'a';
astring->characters = &a;
astring->length = 1;
foo = concatenate(concatenate(slice(foo,0,4),astring),slice(foo,5,foo->length));
ध्यान दें कि समवर्ती को दो बार कहा जाता है जिसका अर्थ है कि पूरे स्ट्रिंग को लूप किया जाना है! इसके लिए C कोड से तुलना करेंbar
ऑपरेशन :
bar->characters[4] = 'a';
उत्परिवर्ती स्ट्रिंग ऑपरेशन स्पष्ट रूप से बहुत तेज है।
निष्कर्ष में: ज्यादातर मामलों में, आप एक अपरिवर्तनीय स्ट्रिंग चाहते हैं। लेकिन अगर आपको एक स्ट्रिंग में बहुत सारे जोड़ और सम्मिलित करने की आवश्यकता है, तो आपको गति के लिए उत्परिवर्तन की आवश्यकता है। यदि आप चाहते हैं कि संगामिति सुरक्षा और कचरा संग्रह लाभ इसके साथ हो, तो कुंजी यह है कि आप अपनी परिवर्तनशील वस्तुओं को स्थानीय तरीके से रखें:
// This will have awful performance if you don't use mutable strings
String join(String[] strings, String separator)
{
StringBuilder mutable;
boolean first = true;
for(int i = 0; i < strings.length; i++)
{
if(!first) first = false;
else mutable.append(separator);
mutable.append(strings[i]);
}
return mutable.toString();
}
चूंकि mutable
ऑब्जेक्ट एक स्थानीय संदर्भ है, इसलिए आपको संगामिति सुरक्षा के बारे में चिंता करने की ज़रूरत नहीं है (केवल एक धागा कभी इसे छूता है)। और चूंकि इसे कहीं और संदर्भित नहीं किया गया है, यह केवल स्टैक पर आवंटित किया गया है, इसलिए जैसे ही फ़ंक्शन कॉल समाप्त हो जाता है (आपको कचरा संग्रह के बारे में चिंता करने की आवश्यकता नहीं है) इसे डीलॉक्लेट किया जाता है। और आपको परिवर्तनशीलता और अपरिवर्तनीयता दोनों के सभी प्रदर्शन लाभ मिलते हैं।