অপরিবর্তনীয় বস্তু এমন একটি বস্তু যেখানে অভ্যন্তরীণ ক্ষেত্রগুলি (বা কমপক্ষে সমস্ত অভ্যন্তরীণ ক্ষেত্রগুলি যা এর বাহ্যিক আচরণকে প্রভাবিত করে) পরিবর্তন করা যায় না।
পরিবর্তনযোগ্য স্ট্রিংগুলির অনেক সুবিধা রয়েছে:
সম্পাদনা: নিম্নলিখিত ক্রিয়াকলাপটি গ্রহণ করুন:
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: যদি একটি অপরিবর্তনীয় বস্তুর অভ্যন্তরীণ গঠন বৈধ, এটা সবসময় কার্যকর থাকবে। বিভিন্ন থ্রেড যে অবজেক্টের মধ্যে একটি অবৈধ অবস্থা তৈরি করতে পারে এমন কোনও সুযোগ নেই। অতএব, অপরিবর্তনীয় জিনিসগুলি থ্রেড নিরাপদ ।
আবর্জনা সংগ্রহ: আবর্জনা সংগ্রহকারীদের পক্ষে অপরিবর্তনীয় বস্তু সম্পর্কে যৌক্তিক সিদ্ধান্ত নেওয়া আরও সহজ।
যাইহোক, অপরিবর্তনীয়তার জন্য ডাউনসাইডগুলিও রয়েছে:
পারফরম্যান্স: দাঁড়াও, আমি ভেবেছিলাম আপনি বলেছিলেন পারফরম্যান্স হ'ল অপরিবর্তনীয়তার sideর্ধ্বমুখী! ঠিক আছে, এটি কখনও কখনও হয়, তবে সবসময় হয় না। নিম্নলিখিত কোড নিন:
foo = foo.substring(0,4) + "a" + foo.substring(5); // foo is a String
bar.replace(4,5,"a"); // bar is a StringBuilder
দুটি রেখা উভয়ই "a" অক্ষর দিয়ে চতুর্থ অক্ষরকে প্রতিস্থাপন করে। কোডের দ্বিতীয় ভাগটি কেবল বেশি পঠনযোগ্য নয়, এটি আরও দ্রুত। Foo এর জন্য আপনাকে অন্তর্নিহিত কোডটি কীভাবে করতে হবে তা দেখুন। সাবস্ট্রিংগুলি সহজ, তবে এখন যেহেতু পাঁচটি স্পেসে ইতিমধ্যে একটি চরিত্র রয়েছে এবং অন্য কোনও কিছু ফুকে রেফারেন্স দিচ্ছে, আপনি কেবল এটি পরিবর্তন করতে পারবেন না; আপনাকে পুরো স্ট্রিংটি অনুলিপি করতে হবে (অবশ্যই এই কার্যকারিতাটির কিছুটি আসল অন্তর্নিহিত সিতে ফাংশনগুলিতে বিমূর্ত করা হয়েছে, তবে এখানে বিন্দুটি কোডটি দেখানো যা সমস্ত এক জায়গায় কার্যকর করা হয়)।
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));
নোট করুন যে সংযুক্তিকে দ্বিগুণ বলা হয় যার অর্থ পুরো স্ট্রিংটি লুপ করা উচিত! 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
অবজেক্টটি স্থানীয় রেফারেন্স, তাই আপনাকে সম্মতি সুরক্ষার বিষয়ে চিন্তা করতে হবে না (কেবলমাত্র একটি থ্রেডই এটি স্পর্শ করে)। এবং যেহেতু এটি অন্য কোথাও উল্লেখ করা হয়নি, এটি কেবল স্ট্যাকের জন্য বরাদ্দ করা হয়েছে, সুতরাং এটি ফাংশন কল শেষ হওয়ার সাথে সাথেই এটি বিচ্ছিন্ন করা হয়েছে (আপনাকে আবর্জনা সংগ্রহের বিষয়ে চিন্তা করতে হবে না)। এবং আপনি উভয় পরিবর্তন এবং অপরিবর্তনীয়তার সমস্ত কার্যকারিতা সুবিধা পান।