در کار یک برنامه نویس، اغلب برخی از کارها یا اجزای آنها ممکن است تکرار شوند. بنابراین، امروز می خواهم به موضوعی بپردازم که اغلب در کار روزانه هر توسعه دهنده جاوا با آن مواجه می شود. فرض کنید رشته خاصی را از یک متد دریافت می کنید. و همه چیز در مورد آن خوب به نظر می رسد، اما چیز کوچکی وجود دارد که برای شما مناسب نیست. به عنوان مثال، جداکننده مناسب نیست و شما به یکی دیگر (یا اصلاً) نیاز دارید. در چنین شرایطی چه می توان کرد؟ طبیعتاً از متدهای
replace
کلاس استفاده کنید String
.
جایگزینی رشته جاوا
شی نوعString
دارای چهار تغییر از روش جایگزینی است replace
:
replace(char, char);
replace(CharSequence, CharSequence);
replaceFirst(String, String);
replaceAll(String, String).
replace(char, char)
String replace(char oldChar, char newChar)
- همه رخدادهای شخصیت آرگومان اول را oldChar
با آرگومان دوم جایگزین می کند newChar
. در این مثال، کاما را با نقطه ویرگول جایگزین می کنیم:
String value = "In JavaRush, Diego the best, Diego is Java God".replace(',', ';');
System.out.println(value);
خروجی کنسول:
In JavaRush; Diego the best; Diego is Java God
2.replace(CharSequence, CharSequence)
هر زیر رشته از یک رشته را که با یک دنباله مشخص از کاراکترها مطابقت دارد، با دنباله ای از کاراکترهای جایگزین جایگزین می کند.
String value = "In JavaRush, Diego the best, Diego is Java God".replace("Java", "Rush");
System.out.println(value);
نتیجه:
In RushRush, Diego the best, Diego is Rush God
3.replaceFirst(String, String)
String replaceFirst(String regex, String replacement)
- اولین رشته فرعی را که با عبارت منظم مشخص شده مطابقت دارد با یک رشته جایگزین جایگزین می کند. وقتی از یک عبارت منظم نامعتبر استفاده می کنید، می توانید PatternSyntaxException را بگیرید (که چیز خوبی نیست). در این مثال، بیایید نام ربات قهرمان را جایگزین کنیم:
String value = "In JavaRush, Diego the best, Diego is Java God".replaceFirst("Diego", "Amigo");
System.out.println(value);
خروجی کنسول:
In JavaRush, Amigo the best, Diego is Java God
همانطور که می بینیم، فقط اولین ورودی "دیگو" تغییر کرده است، اما موارد بعدی کنار گذاشته شده اند - یعنی دست نخورده. 4. replaceAll()
در جاوا String replaceAll(String regex, String replacement)
- این روش همه رخدادهای یک رشته فرعی در یک رشته regex
را با replacement
. یک عبارت منظم می تواند به عنوان اولین آرگومان استفاده شود regex
. به عنوان مثال، بیایید سعی کنیم جایگزین قبلی را با نام ها انجام دهیم، اما با یک روش جدید:
String value = "In JavaRush, Diego the best, Diego is Java God".replaceAll("Diego", "Amigo");
System.out.println(value);
خروجی کنسول:
In JavaRush, Amigo the best, Amigo is Java God
همانطور که می بینیم، تمام نمادها به طور کامل با نمادهای ضروری جایگزین شده اند. من فکر می کنم آمیگو راضی خواهد شد =)
عبارات با قاعده
در بالا گفته شد که می توان با استفاده از یک عبارت منظم جایگزین کرد. ابتدا بیایید خودمان روشن کنیم که عبارت منظم چیست؟ عبارات منظم یک زبان رسمی برای جستجو و دستکاری زیر رشته ها در متن، بر اساس استفاده از متاکاراکترها (کاراکترهای عام) هستند. به عبارت ساده، الگویی از کاراکترها و فراکاراکترها است که یک قانون جستجو را تعریف می کند. به عنوان مثال:\D
- الگویی که هر کاراکتر غیر دیجیتالی را توصیف می کند. \d
- هر کاراکتر عددی را تعریف می کند که می تواند به صورت [0-9]
; [a-zA-Z]
- الگویی که نویسههای لاتین از a تا z را توصیف میکند، به حروف بزرگ و کوچک حساس نیست. برنامه را در متد replaceAll
کلاس در نظر بگیرید String
:
String value = "In JavaRush, Diego the best, Diego is Java God".replaceAll("\\s[a-zA-Z]{5}\\s", " Amigo ");
System.out.println(value);
خروجی کنسول:
In JavaRush, Amigo the best, Amigo is Java God
\\s[a-zA-Z]{5}\\s
- کلمه ای از 5 کاراکتر لاتین را توصیف می کند که با فاصله احاطه شده است. بر این اساس، این الگو با رشته ای که از آن عبور کردیم جایگزین می شود.
جاوا regex جایگزین
اساسا برای استفاده از عبارات منظم در جاوا، قابلیت هایjava.util.regex
. کلاس های کلیدی عبارتند از:
Pattern
- کلاسی که یک نسخه کامپایل شده از یک عبارت منظم را ارائه می دهد.Matcher
- این کلاس الگو را تفسیر می کند و مطابقت ها را در رشته ای که دریافت می کند تعیین می کند.
Matcher
و Pattern
:
Pattern pattern = Pattern.compile("\\s[a-zA-Z]{5}\\s");
Matcher matcher = pattern.matcher("In JavaRush, Diego the best, Diego is Java God");
String value = matcher.replaceAll(" Amigo ");
System.out.println(value);
و نتیجه گیری ما یکسان خواهد بود:
In JavaRush, Amigo the best, Amigo is Java God
در این مقاله می توانید در مورد عبارات منظم بیشتر بخوانید .
جایگزین جایگزینی همه
شکی نیست که روش ها بسیار چشمگیرreplace
هستند String
، اما نمی توان این واقعیت را نادیده گرفت که یک شی String
است immutable
، یعنی بعد از ایجاد آن قابل تغییر نیست. بنابراین، هنگامی که برخی از قسمتهای یک رشته را با استفاده از متدها جایگزین میکنیم replace
، شی را تغییر نمیدهیم String
، بلکه هر بار یک شی جدید با محتویات لازم ایجاد میکنیم. اما ایجاد یک شی جدید هر بار زمان زیادی می برد، اینطور نیست؟ مخصوصاً وقتی سؤال چند شیء نیست، بلکه چند صد یا حتی هزاران است. خواه ناخواه، شما شروع به فکر کردن در مورد جایگزین می کنید. و چه جایگزین هایی داریم؟ هوم... وقتی صحبت از String
ویژگی آن می شود immutable
، بلافاصله به گزینه های جایگزین فکر می کنید، اما نه immutable
، یعنی StringBuilder/StringBuffer . همانطور که به یاد داریم، این کلاس ها در واقع تفاوتی ندارند، به جز اینکه StringBuffer
برای استفاده در یک محیط چند رشته ای بهینه شده اند، بنابراین StringBuilder
در استفاده تک رشته ای تا حدودی سریعتر کار می کنند. بر این اساس، امروز ما StringBuilder.
از این کلاس استفاده خواهیم کرد که روش های جالب زیادی دارد، اما به طور خاص در حال حاضر ما به replace
. StringBuilder replace(int start, int end, String str)
- این روش کاراکترهای یک رشته فرعی از این دنباله را با کاراکترهای رشته مشخص شده جایگزین می کند. رشته فرعی از ابتدای مشخص شده شروع می شود و تا کاراکتر انتهای فهرست -1
یا تا پایان دنباله در صورت وجود چنین کاراکتری ادامه می یابد. بیایید به یک مثال نگاه کنیم:
StringBuilder strBuilder = new StringBuilder("Java Rush");
strBuilder.replace(5, 9, "God");
System.out.println(strBuilder);
نتیجه:
Java God
همانطور که می بینید، بازه ای را که می خواهیم رشته را در آن بنویسیم، نشان می دهیم و یک رشته فرعی در بالای آنچه در فاصله است می نویسیم. بنابراین، با استفاده از کمک، StringBuilder
یک آنالوگ از روش را دوباره ایجاد می کنیم replaceall java
. چگونه به نظر خواهد رسید:
public static String customReplaceAll(String str, String oldStr, String newStr) {
if ("".equals(str) || "".equals(oldStr) || oldStr.equals(newStr)) {
return str;
}
if (newStr == null) {
newStr = "";
}
final int strLength = str.length();
final int oldStrLength = oldStr.length();
StringBuilder builder = new StringBuilder(str);
for (int i = 0; i < strLength; i++) {
int index = builder.indexOf(oldStr, i);
if (index == -1) {
if (i == 0) {
return str;
}
return builder.toString();
}
builder = builder.replace(index, index + oldStrLength, newStr);
}
return builder.toString();
}
در نگاه اول ترسناک است، اما با کمی درک می توانید درک کنید که همه چیز چندان پیچیده و کاملا منطقی نیست. ما سه استدلال داریم:
str
- خطی که در آن می خواهیم چند رشته فرعی را جایگزین کنیم.oldStr
- نمایش زیر رشته هایی که جایگزین خواهیم کرد.newStr
- با چه چیزی جایگزین خواهیم کرد.
if
ما به اولین مورد نیاز داریم تا داده های دریافتی را بررسی کند، و اگر رشته خالی str
باشد oldStr
یا زیررشته جدید newStr
برابر با رشته قبلی باشد oldStr
، اجرای متد بی معنی خواهد بود. بنابراین، رشته اصلی - str
. در مرحله بعد، ما newStr
را بررسی می کنیم null
، و اگر این مورد باشد، آن را به یک قالب رشته خالی تبدیل می کنیم که برای ما راحت تر است - ""
. سپس اعلان متغیرهای مورد نیاز خود را داریم:
- طول کل رشته
str
; - طول زیر رشته
oldStr
; - شی
StringBuilder
از یک رشته مشترک
StringBuilder
- indexOf
- شاخص اولین رخداد رشته فرعی مورد نظر خود را می یابیم. متأسفانه، میخواهم توجه داشته باشم که indexOf
با عبارات منظم کار نمیکند، بنابراین روش نهایی ما فقط با وقوع رشتهها کار میکند (( اگر این شاخص برابر باشد -1
، پس دیگر از این رخدادها در شی فعلی وجود نخواهد داشت StringBuilder
. بنابراین با نتیجه علاقه از روش خارج میشویم: در ما موجود است StringBuilder
که String
با استفاده از آن به آن تبدیل میکنیم toString
. اگر شاخص ما -1
در اولین تکرار حلقه برابر باشد، رشته فرعی که باید جایگزین شود در حالت کلی نبوده است. بنابراین در چنین شرایطی ما به سادگی رشته کلی را برمی گردانیم و سپس از روشی که در بالا توضیح داده شد replace
برای StringBuilder
استفاده از اندیس پیدا شده رخداد برای نشان دادن مختصات رشته فرعی که باید جایگزین شود استفاده می شود.این حلقه اجرا خواهد شد. اگر رشته فقط از کاراکتری تشکیل شده باشد که باید جایگزین شود، در این صورت است که حلقه به طور کامل اجرا می شود و نتیجه را StringBuilder
به رشته تبدیل می کنیم. باید صحت این روش را بررسی کنیم، درست است؟ بیایید تستی بنویسیم که عملکرد روش را در موقعیت های مختلف بررسی می کند:
@Test
public void customReplaceAllTest() {
String str = "qwertyuiop__qwertyuiop__";
String firstCase = Solution.customReplaceAll(str, "q", "a");
String firstResult = "awertyuiop__awertyuiop__";
assertEquals(firstCase, firstResult);
String secondCase = Solution.customReplaceAll(str, "q", "ab");
String secondResult = "abwertyuiop__abwertyuiop__";
assertEquals(secondCase, secondResult);
String thirdCase = Solution.customReplaceAll(str, "rtyu", "*");
String thirdResult = "qwe*iop__qwe*iop__";
assertEquals(thirdCase, thirdResult);
String fourthCase = Solution.customReplaceAll(str, "q", "");
String fourthResult = "wertyuiop__wertyuiop__";
assertEquals(fourthCase, fourthResult);
String fifthCase = Solution.customReplaceAll(str, "uio", "");
String fifthResult = "qwertyp__qwertyp__";
assertEquals(fifthCase, fifthResult);
String sixthCase = Solution.customReplaceAll(str, "", "***");
assertEquals(sixthCase, str);
String seventhCase = Solution.customReplaceAll("", "q", "***");
assertEquals(seventhCase, "");
}
را می توان به 7 تست مجزا تقسیم کرد که هر کدام مسئول مورد آزمایشی خود خواهند بود. پس از راه اندازی آن، خواهیم دید که سبز است، یعنی موفق است. خب، به نظر می رسد که تمام است. اگرچه صبر کنید، اما در بالا گفتیم که این روش بسیار سریعتر replaceAll
از String
. خوب بگذار ببینیم:
String str = "qwertyuiop__qwertyuiop__";
long firstStartTime = System.nanoTime();
for (long i = 0; i < 10000000L; i++) {
str.replaceAll("tyu", "#");
}
double firstPerformance = System.nanoTime() - firstStartTime;
long secondStartTime = System.nanoTime();
for (long i = 0; i < 10000000L; i++) {
customReplaceAll(str, "tyu", "#");
}
double secondPerformance = System.nanoTime() - secondStartTime;
System.out.println("Performance ratio - " + firstPerformance / secondPerformance);
در مرحله بعد، این کد سه بار اجرا شد و به نتایج زیر رسیدیم: خروجی کنسول:
Performance ratio - 5.012148941181627
Performance ratio - 5.320637176017641
Performance ratio - 4.719192686500394
همانطور که می بینیم، به طور متوسط روش ما 5 برابر بیشتر از replaceAll
کلاس کلاسیک کارآمدتر است! String
خوب، در نهایت، بیایید همان چک را اجرا کنیم، اما، به اصطلاح، بیهوده. به عبارت دیگر در موردی که مطابقت پیدا نشود. بیایید رشته جستجو را از "tyu"
به جایگزین کنیم "--"
. سه اجرا نتایج زیر را به همراه داشت: خروجی کنسول:
Performance ratio - 8.789647093542246
Performance ratio - 9.177105482660881
Performance ratio - 8.520964375227406
به طور متوسط، عملکرد برای مواردی که هیچ مسابقه ای پیدا نشد، 8.8 برابر افزایش یافت!
GO TO FULL VERSION