في عمل المبرمج، في كثير من الأحيان قد تتكرر بعض المهام أو مكوناتها. لذلك، أود اليوم أن أتطرق إلى موضوع غالبا ما يتم مواجهته في العمل اليومي لأي مطور جافا. لنفترض أنك تتلقى سلسلة معينة من طريقة معينة. ويبدو أن كل شيء فيه جيد، ولكن هناك بعض الأشياء الصغيرة التي لا تناسبك. على سبيل المثال، الفاصل غير مناسب، وتحتاج إلى بعض الآخر (أو لا على الإطلاق). ما الذي يمكن عمله في مثل هذه الحالة؟ وبطبيعة الحال، استخدم أساليب
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()
في Java 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]
- قالب يصف الأحرف اللاتينية من الألف إلى الياء، غير حساس لحالة الأحرف؛ النظر في التطبيق في طريقة 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 أحرف لاتينية محاطة بمسافات. وبناء على ذلك، يتم استبدال هذا القالب بالسلسلة التي مررناها.
استبدال جافا ريجكس
بشكل أساسي، لاستخدام التعبيرات العادية في Java، فإن إمكانيات ملف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
كما نرى، في المتوسط، طريقتنا أكثر إنتاجية بخمس مرات من replaceAll
الطبقة الكلاسيكية! String
حسنًا، أخيرًا، لنجري نفس الفحص، ولكن، إذا جاز التعبير، دون جدوى. بمعنى آخر، في حالة عدم العثور على تطابق. لنستبدل سلسلة البحث من "tyu"
إلى "--"
. أسفرت ثلاث عمليات تشغيل عن النتائج التالية: إخراج وحدة التحكم:
Performance ratio - 8.789647093542246
Performance ratio - 9.177105482660881
Performance ratio - 8.520964375227406
في المتوسط، ارتفع الأداء في الحالات التي لم يتم العثور على تطابقات فيها بمقدار 8.8 مرة!
GO TO FULL VERSION