در این مقاله ی آموزشی در تارنمای بیاموز، به بررسی کلاس StringBuilder در جاوا می پردازیم و متدها و خاصیت های مربوط به آن را توضیح داده و شرح می دهیم. بنابراین اگر قصد دارید در مورد کلاس StringBuilder در زبان برنامه نویسی جاوا، نکات مهم و بیشتری را بیاموزید، این مقاله را تا انتها مطالعه کنید. 


در زبان برنامه نویسی جاوا، مقدار یک رشته، پس از اینکه ایجاد شد، ثابت(fixed) است. رشته ها تغییرناپذیر یا غیر قابل تغییر هستند. وقتی که داشته باشیم someString = "Hello"; و به دنبال آن، داشته باشیم someString = "Goodbye"; درواقع ما محتوای این آدرس از حافظه ی رایانه، که توسط someString ارائه شده است  را تغییر نمی دهیم و کاراکترهای حذف شده ی "Hello" را نیز تغییر نمی دهیم. بلکه به جای آن، عبارت"Goodbye" را در یک موقعیت جدید از حافظه ی رایانه قرار می دهیم و آدرس جدید ایجاد شده را در متغیر someString ذخیره می کنیم. 

اگر بخواهیم مقدار متغیر someString را از "Goodbye" به "Goodbye Everybody" تغییر دهیم، نمی توانیم یک فاصله ی سفید و "Everybody" را به متغیر someString که حاوی "Goodbye" است، اضافه کنیم. بلکه به جای آن، باید کاملاً یک رشته ی جدید "Goodbye Everybody" را ایجاد کنیم، و آدرس آن را در متغیر someString قرار دهیم. 

برای دور زدن این محدودیت ها، می توانیم از کلاس های StringBuilder و StringBuffer استفاده کنیم. وقتی که می خواهیم یک رشته را اصلاح کنیم، می توانیم از یکی از این کلاس ها استفاده کنیم؛ این کلاس ها، جایگزینی برای کلاس String هستند. معمولاً می توانیم از یک آبجکت StringBuilder یا StringBuffer در هرجایی که یک رشته را به کار می بریم، استفاده کنیم. 

مانند کلاس String، این دو کلاس نیز بخشی از پکیج java.lang هستند و به طور اتوماتیک در هر برنامه ای اضافه(import) می شوند. این کلاس ها با یکدیگر یکسان هستند، به جز در موارد زیر:

  1. کلاس StringBuilder موثرتر است.
  2. کلاس StringBuffer  به صورت thread safe است. این یعنی باید از آن در اپلیکیشن هایی استفاده شود که چندین تِرِد را اجرا می کنند. تِرِدها، راه های کنترلی  یا دستوراتی هستند که در حین اجرای برنامه اتخاذ می شوند. چونکه اکثر برنامه هایی که ما می نویسیم( و تمام برنامه هایی که تا اینجا نوشته ایم) حاوی یک تِرِد تکی بوده اند، معمولاً باید از کلاس StringBuilder استفاده کنیم.

در بقیه ی این بخش، در مورد کلاس StringBuilder توضیح می دهیم اما تمام این دستورات، برای کلاس StringBuffer نیز صحیح هستند.  بعنوان مثال، برای استفاده از کلاس StringBuilder، می توانیم یک آبجکت از آن ایجاد کنیم، که حاوی یک رشته(String) است، مانند زیر :

StringBuilder message = new StringBuilder("Hello there");

وقتی که یک رشته ایجاد کنیم، می توانیم کلمه ی کلیدی new را از قلم بیندازیم. اما هنگامی که داریم یک آبجکت از کلاس StringBuilder را مقدار دهی اولیه می کنیم، باید از کلمه ی کلیدی new و نام کانستراکتور مورد نظر استفاده کنیم؛ و یک مقدار اولیه را بین پرانتزهای کانستراکتور قرار دهیم. برای ایجاد یک متغیر StringBuilder خالی، می توانیم از دستوری مانند زیر استفاده کنیم:

StringBuilder uninitializedString = null;

این متغیر تا زمانی که آن را با یک آبجکت StringBuilder مقدار دهی اولیه(initialize) نکنیم، به چیزی اشاره نمی کند. به طور کلی، وقتی که یک آبجکت رشته(string) ایجاد می کنیم، حافظه ی کافی برای جا دادن به کاراکترهای یونیکد در این رشته اختصاص داده می شود. 

اما یک آبجکت StringBuilder ، حاوی یک بلوک از حافظه است که بافر(buffer) نامیده می شود؛ که ممکن است حاوی یک رشته باشد، یا نباشد. حتی اگر بافر حاوی یک رشته باشد، امکان ندارد این رشته، کل بافر را اشغال کند. به عبارت دیگر، طول یک رشته می تواند با طول بافر متفاوت باشد. طول واقعی بافر، برابر با ظرفیت آبجکت StringBuilder است. 

ما می توانیم طول یک رشته را در یک آبجکت StringBuilder با استفاده از متد setLength() تغییر دهیم. پروپرتی length یک خصوصیت از کلاس StringBuilder است که تعداد کاراکترهای درونِ رشته ای که در StringBuilder قرار گرفته است را شناسایی می کند. وقتی که طول(length) یک آبجکت StringBuilder را افزایش دهیم، به طوری که طولانی تر از رشته ای باشد که در آن قرار دارد، کاراکترهای اضافی، حاوی مقدار '\u0000' یعنی کاراکتر null خواهند شد. اگر بخواهیم با استفاده از متد setLength()، یک طول کوتاه تر نسبت به رشته را مشخص کنیم، این رشته کوتاه می شود. 

برای پیدا کردن ظرفیت یک آبجکت StringBuilder، می توانیم از متد capacity() استفاده کنیم. اپلیکیشن StringBuilderDemo که در تصویر 7.12 نشان داده شده است، کار با این متد را نشان می دهد. این اپلیکیشن، یک آبجکت به نام nameString ایجاد می کند که حاوی هفت کاراکتر "Barbara" می باشد. سپس ظرفیت آبجکت StringBuilder، به دست می آید و در یک متغیر از نوع integer به نام nameStringCapacity ذخیره می شود و نمایش می یابد. 

(تصویر 7.12 : اپلیکیشن StringBuilderDemo)

یک اپلیکیشن در مورد کلاس StringBuilder در جاواتصویر 7.13 نشان می دهد که ظرفیت StringBuilder برابر با 23  است؛ که 16 کاراکتر بیشتر از طول(length) رشته ی"Barbara" است. هنگامی که یک آبجکت StringBuilder ایجاد می کنیم، ظرفیت آن برابر با طول رشته ای است که در StringBuilder قرار دارد، بعلاوه ی 16. این 16 موقعیت اضافی، امکان اصلاح آبجکت StringBuilder را پس از ایجاد، بدون اختصاص هیچ مکان حافظه جدیدی، فراهم می‌کند.

(تصویر 7.13 : اپلیکیشن StringBuilderDemo)


نکته: سازندگان جاوا، 16 کاراکتر را بعنوان طول(length) اضافی برای آبجکت StringBuilder در نظر گرفته اند. زیرا این 16 کاراکتر، به طور کامل چهار بایت از حافظه را اشغال می کند. همان طور که بیشتر با رایانه کار می کنید و به طور خاص، بیشتر برنامه نویسی می کنید، متوجه می شوید که ظرفیت‌های ذخیره‌سازی تقریباً همیشه به صورت توان هایی از 2 هستند؛ مانند 4 و 8 و 16 و 32 و 64 و همین طور الی آخر. 


در اپلیکیشن نشان داده شده در تصویر 7.12، متغیر addressString به صورت StringBuilder addressString = null; ایجاد شده است. این متغیر به چیزی اشاره نمی کند، تا زمانی که در دستور زیر، با آبجکت تعریف شده ی StringBuilder مقدار دهی اولیه شود:

addressString = new StringBuilder("6311 Hickory Nut Grove Road");

ظرفیت(capacity) این آبجکت StringBuilder جدید، در تصویر 7.13 به صورت طول رشته بعلاوه ی 16 که می شود 43 نشان داده شده است. در اپلیکیشن نشان داده شده در تصویر 7.12، با استفاده از متد setLength()، طول هریک از رشته ها، به 20 تغییر داده شده است. این اپلیکیشن nameString گسترش یافته و عبارت "end" را نمایش می دهد. بنابراین در خروجی می توانید ببینید که 13 فاصله ی سفید اضافی در انتهای رشته ی مورد نظر وجود دارد. این اپلیکیشن همچنین addressString کوتاه شده را نیز نمایش می دهد؛ بنابراین می توانیم تاثیر کاهش دادن طول آن به 20 را مشاهده کنیم. 

با استفاده از آبجکت های StringBuilder، کارایی رایانه در آبجکت های رشته(String) بهبود پیدا می کند؛ زیرا می توانیم محتواهای جدید را در یک StringBuilder درج یا ضمیمه کنیم. به عبارت دیگر، برخلاف رشته های تغییر ناپذیر، قابلیت StringBuilder اصلاح شده است و وقتی که بدانیم محتوای رشته باید تغییر کند، باعث کارایی بیشتر آنها می شود. 


نکته: با اینکه متد equals() محتوای آبجکت های رشته ها را مقایسه می کند، وقتی که از آن در آبجکت های StringBuilder استفاده کنیم، مرجع ها(references) را با هم مقایسه می کند. برای مقایسه ی دو آبجکت StringBuilder می توانیم آنها را به رشته(String) تبدیل کنیم؛ مانند زیر:

obj1.toString().equals(obj2.toString())

کلاس StringBuilder چهار کانستراکتور را به صورت زیر به ما ارائه می دهد:

  1. public StringBuilder() : یک StringBuilder می سازد که کاراکتری ندارد و سایز پیش فرض آن 16 کاراکتر است.
  2. public StringBuilder(int capacity) : یک StringBuilder می سازد که که کاراکتری ندارد و یک capacity در پارامتر آن مشخص شده است.
  3. public StringBuilder(String s) : حاوی همان کاراکترهایی است که در آبجکت های رشته(String) ذخیره شده اند. ظرفیت(capacity) این StringBuilder برابر با طول آرگومان String ای است که ما ارائه داده ایم، بعلاوه ی 16 کاراکتر اضافی.
  4. چهارمین کانستراکتور StringBuilder، از یک آرگومان از نوع CharSequence استفاده می کند. CharSequence یک کلاس دیگر در جاوا است. و یک اینترفیس(interfaces) است که دنباله ای از مقادیر کاراکتری(char values) را در خود نگهداری می کند. در فصل "مفاهیم وراثت پیشرفته" در مورد ایجاد اینترفیس ها نکات بیشتری خواهید آموخت. 

متد append() به ما امکان می دهد تا کاراکترهایی را به انتهای آبجکت StringBuilder اضافه کنیم. بعنوان مثال، در دو دستور زیر، یک عبارت به نام phrase ایجاد می کنیم که حاوی مقدار "Happy" است و این عبارت را به "Happy birthday" تغییر می دهیم:

StringBuilder phrase = new StringBuilder("Happy");
phrase.append(" birthday");

متد insert() به ما امکان می دهد تا در داخل یک آبجکت StringBuilder، کاراکترهایی را در یک موقعیت خاص اضافه کنیم. بعنوان مثال، اگر phrase برابر با "Happy birthday" باشد، آنگاه دستور phrase.insert(6, "30th ") باعث می شود، این StringBuilder برابر با "Happy 30th birthday" شود. موقعیت یا اندیس اولین کاراکتر در این عبارت، از 0 شروع می شود. 

اگر بخواهیم فقط یک کاراکتر را در یک StringBuilder تغییر دهیم، می توانیم از متد setCharAt() استفاده کنیم؛ که به ما امکان می دهد تا یک کاراکتر را در یک موقعیت مشخص شده در داخل یک آبجکت StringBuilder تغییر دهیم. این متد به دو آرگومان نیاز دارد: یک موقعیت(اندیس) و یک کاراکتر. اگر متغیر phrase برابر با "Happy 30th birthday" باشد، آنگاه دستور phrase.setCharAt(6,'4') این عبارت را برابر با "40th birthday greeting" می کند. 


یکی از راه هایی که می توانیم یک کاراکتر را از یک آبجکت StringBuilder استخراج کنیم، این است که از متد charAt() استفاده کنیم. متد charAt() یک آرگومان می پذیرد که اندیس کاراکتر مورد نظر از ابتدای رشته مشخص می کند و از آن اندیس، کاراکتر مورد نظر را برمی گرداند. در دستورات زیر، کاراکتر 'p' را به متغیر letter انتساب می دهیم:

StringBuilder text = new StringBuilder("Java Programming");
char letter = text.charAt(5);

اگر سعی کنید از یک اندیس استفاده کنید که کوچکتر از 0 باشد، یا بزرگتر از اندیس آخرین موقعیت در آبجکت StringBuilder باشد، باعث می شوید یک خطای شناخته شده ی an exception ایجاد شود و برنامه ی شما خاتمه می یابد. 

وقتی که ما سایز نهایی مورد نیاز برای یک آبجکت StringBuilder را تقریب می زنیم، اختصاص ظرفیت کافی باعث می شود کارایی برنامه بهبود پیدا کند. بعنوان مثال، برنامه ی نشان داده شده در تصویر 7.14، زمان مورد نیاز برای اضافه کردن "Java" به تعداد 20,000 بار در دو آبجکت StringBuilder را مورد مقایسه قرار می دهد. سایز اولیه ی آبجکت اول، برابر با مقدار پیش فرض، یعنی 16 کاراکتر است و سایز اولیه ی آبجکت دوم، 80,000 کاراکتر می باشد. تصویر 7.15 اجرای این برنامه را نشان می دهد. 

همان طور که مشاهده می کنید، برای حلقه ی اول که از StringBuilder کوتاه تر استفاده می کند، زمان اضافه تری نیاز است. این زمان، به خاطر این است که با افزایش اندازه ی آبجکت، مرتباً حافظه ی جدید اختصاص می یابد:

(تصویر 7.14 : اپلیکیشن CompareConcatenationTimes)

یک اپلیکیشن کلاس StringBuilder در جاوا

(تصویر 7.15: خروجی برنامه ی CompareConcatenationTimes)

خروجی برنامه کلاس StringBuilder در جاوا


نکته: شما در فصل 6، با متد currentTimeMillis() آشنا شده اید. 


نکته: متدهای اضافی زیادی برای String و StringBuilder وجود دارند. برای اطلاعات بیشتر به تارنمای جاوا مراجعه کنید. سپس مشخصات فنی و ورژن جاوا که از آن استفاده می کنید را وارد کنید؛ سپس از لیست مورد نظر هر کلاسی که می خواهید را انتخاب کنید تا در مورد متدهای آن نکاتی را بیاموزید. 


دو عبارت صحیح و یک عبارت اشتباه

موضوع: یادگیری کلاس های StringBuilder و StringBuffer

1. وقتی که ما یک رشته(String) ایجاد می کنیم، می توانیم کلمه ی کلیدی new را از قلم بیندازیم، اما وقتی که ما یک آبجکت StringBuilder را مقدار دهی اولیه می کنیم، باید از کلمه ی کلیدی new سپس از نام کانستراکتور و سپس از یک مقدار بین پرانتزهای کانستراکتور استفاده کنیم. 

2. وقتی که ما یک آبجکت StringBuilder ایجاد می کنیم، که یک مقدار اولیه به نام "Juan" داشته باشد، ظرفیت(capacity) این آبجکت 16 می باشد. 

3. اگر یک آبجکت StringBuilder به نام myAddress حاوی "817" باشد، آنگاه دستور myAddress.append(" Maple Lane"); مقدار myAddress را تغییر می دهد به طوری که برابر با "817 Maple Lane" می شود. 

پاسخ:

عبارت شماره 2 اشتباه است. وقتی که ما یک آبجکت StringBuilder با مقدار اولیه ی "Juan" ایجاد کنیم، ظرفیت(capacity) آن برابر با طول رشته ای که در StringBuilder قرار دارد،  یعنی 4 بعلاوه ی 16، که برابر با 20 می شود، خواهد بود.