JavaRush /Blog Java /Random-MS /Gantikan rentetan dalam Java

Gantikan rentetan dalam Java

Diterbitkan dalam kumpulan
Dalam kerja pengaturcara, selalunya beberapa tugas atau komponennya mungkin diulang. Oleh itu, hari ini saya ingin menyentuh topik yang sering ditemui dalam kerja harian mana-mana pembangun Java. Gantikan rentetan dalam Java - 1Mari kita anggap bahawa anda menerima rentetan tertentu daripada kaedah tertentu. Dan segala-galanya tentangnya nampaknya bagus, tetapi ada beberapa perkara kecil yang tidak sesuai dengan anda. Sebagai contoh, pemisah tidak sesuai, dan anda memerlukan yang lain (atau tidak sama sekali). Apa yang boleh dilakukan dalam keadaan sedemikian? Sememangnya, gunakan kaedah replacekelas String.

Gantikan rentetan Java

Objek jenis Stringmempunyai empat variasi kaedah penggantian replace:
  • replace(char, char);
  • replace(CharSequence, CharSequence);
  • replaceFirst(String, String);
  • replaceAll(String, String).
Tujuan semua kaedah ini adalah sama - menggantikan sebahagian daripada rentetan dengan rentetan lain. Mari kita lihat mereka dengan lebih dekat. 1.replace(char, char) String replace(char oldChar, char newChar) - menggantikan semua kemunculan watak hujah pertama oldChardengan yang kedua - newChar. Dalam contoh ini, kami akan menggantikan koma dengan koma bertitik:
String value = "In JavaRush, Diego the best, Diego is Java God".replace(',', ';');
System.out.println(value);
Output konsol:
In JavaRush; Diego the best; Diego is Java God
2.replace(CharSequence, CharSequence) Menggantikan setiap subrentetan rentetan yang sepadan dengan jujukan aksara yang ditentukan dengan jujukan aksara gantian.
String value = "In JavaRush, Diego the best, Diego is Java God".replace("Java", "Rush");
System.out.println(value);
Kesimpulan:
In RushRush, Diego the best, Diego is Rush God
3.replaceFirst(String, String) String replaceFirst(String regex, String replacement) - Menggantikan subrentetan pertama yang sepadan dengan ungkapan biasa yang ditentukan dengan rentetan gantian. Apabila menggunakan ungkapan biasa yang tidak sah, anda boleh menangkap PatternSyntaxException (yang bukan perkara yang baik). Dalam contoh ini, mari kita gantikan nama robot juara:
String value = "In JavaRush, Diego the best, Diego is Java God".replaceFirst("Diego", "Amigo");
System.out.println(value);
Output konsol:
In JavaRush, Amigo the best, Diego is Java God
Seperti yang dapat kita lihat, hanya kejadian pertama "Diego" telah berubah, tetapi yang berikutnya tetap ditinggalkan—iaitu, tidak disentuh. 4. replaceAll()dalam Java String replaceAll(String regex, String replacement) - kaedah ini menggantikan semua kejadian subrentetan dalam rentetan regexdengan replacement. Ungkapan biasa boleh digunakan sebagai hujah pertama regex. Sebagai contoh, mari cuba lakukan penggantian sebelumnya dengan nama, tetapi dengan kaedah baharu:
String value = "In JavaRush, Diego the best, Diego is Java God".replaceAll("Diego", "Amigo");
System.out.println(value);
Output konsol:
In JavaRush, Amigo the best, Amigo is Java God
Seperti yang kita lihat, semua simbol telah diganti sepenuhnya dengan yang diperlukan. Saya rasa Amigo akan berpuas hati =)

Ungkapan Biasa

Dikatakan di atas bahawa adalah mungkin untuk menggantikan menggunakan ungkapan biasa. Mula-mula, mari kita jelaskan sendiri apa itu ungkapan biasa? Ungkapan biasa ialah bahasa formal untuk mencari dan memanipulasi subrentetan dalam teks, berdasarkan penggunaan aksara meta (wildcard). Ringkasnya, ia adalah corak aksara dan metakarakter yang mentakrifkan peraturan carian. Contohnya: \D- templat yang menerangkan sebarang aksara bukan digital; \d— mentakrifkan sebarang aksara angka, yang juga boleh diterangkan sebagai [0-9]; [a-zA-Z]— templat yang menerangkan aksara Latin dari a hingga z, tidak peka huruf besar-besaran; Pertimbangkan aplikasi dalam kaedah replaceAllkelas String:
String value = "In JavaRush, Diego the best, Diego is Java God".replaceAll("\\s[a-zA-Z]{5}\\s", " Amigo ");
System.out.println(value);
Output konsol:
In JavaRush, Amigo the best, Amigo is Java God
\\s[a-zA-Z]{5}\\s— menerangkan perkataan 5 aksara Latin yang dikelilingi oleh ruang. Sehubungan itu, templat ini digantikan dengan rentetan yang kami lalui.

Gantikan regex Java

Pada asasnya, untuk menggunakan ungkapan biasa dalam Java, keupayaan java.util.regex. Kelas utama ialah:
  1. Pattern- kelas yang menyediakan versi terkumpul ungkapan biasa.
  2. Matcher— kelas ini mentafsir corak dan menentukan padanan dalam rentetan yang diterimanya.
Biasanya, kedua-dua kelas ini berfungsi bersama. Jadi, apakah rupa objek kita sebelum ini, tetapi dengan bantuan Matcherdan 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);
Dan kesimpulan kami akan sama:
In JavaRush, Amigo the best, Amigo is Java God
Anda boleh membaca lebih lanjut tentang ungkapan biasa dalam artikel ini .

Alternatif untuk menggantikanSemua

Tidak ada keraguan bahawa kaedahnya replacesangat Stringmengagumkan, tetapi seseorang tidak boleh mengabaikan fakta bahawa Stringia adalah immutableobjek, iaitu, ia tidak boleh diubah selepas penciptaannya. Oleh itu, apabila kami menggantikan beberapa bahagian rentetan menggunakan kaedah replace, kami tidak menukar objek String, tetapi mencipta yang baharu setiap kali, dengan kandungan yang diperlukan. Tetapi mencipta objek baharu setiap kali mengambil masa yang lama, bukan? Terutama apabila soalan itu bukan beberapa objek, tetapi beberapa ratus, atau bahkan beribu-ribu. Mahu tidak mahu, anda mula memikirkan alternatif. Dan apakah alternatif yang kita ada? Gantikan rentetan dalam Java - 2Hmm... Apabila ia berkaitan dengan Stringproperty immutable, anda segera memikirkan alternatif, tetapi tidak immutable, iaitu StringBuilder/StringBuffer . Seperti yang kita ingat, kelas ini sebenarnya tidak berbeza, kecuali StringBufferia dioptimumkan untuk digunakan dalam persekitaran berbilang benang, jadi StringBuilderia berfungsi dengan lebih pantas dalam penggunaan benang tunggal. Berdasarkan ini, hari ini kita akan menggunakan StringBuilder. Kelas ini mempunyai banyak kaedah yang menarik, tetapi khususnya sekarang kita berminat dengan replace. StringBuilder replace(int start, int end, String str)— kaedah ini menggantikan aksara dalam subrentetan urutan ini dengan aksara dalam rentetan yang ditentukan. Subrentetan bermula pada permulaan yang ditentukan dan berterusan sehingga aksara pada penghujung indeks, -1atau sehingga penghujung jujukan jika tiada aksara sedemikian wujud. Mari lihat contoh:
StringBuilder strBuilder = new StringBuilder("Java Rush");
strBuilder.replace(5, 9, "God");
System.out.println(strBuilder);
Kesimpulan:
Java God
Seperti yang anda lihat, kami menunjukkan selang di mana kami ingin menulis rentetan, dan menulis subrentetan di atas apa yang ada dalam selang itu. Jadi, menggunakan bantuan, StringBuilderkami akan mencipta semula analog kaedah replaceall java. Bagaimanakah ia akan kelihatan seperti:
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();
}
Ia menakutkan pada pandangan pertama, tetapi dengan sedikit pemahaman anda boleh memahami bahawa segala-galanya tidak begitu rumit dan agak logik. Kami mempunyai tiga hujah:
  • str— baris di mana kita ingin menggantikan beberapa subrentetan;
  • oldStr— perwakilan subrentetan yang akan kami gantikan;
  • newStr- apa yang akan kami gantikan.
ifKami memerlukan yang pertama untuk menyemak data masuk, dan jika rentetan itu strsama ada oldStrkosong, atau subrentetan baharu newStrsama dengan yang lama oldStr, maka melaksanakan kaedah itu tidak bermakna. Oleh itu, kami mengembalikan rentetan asal - str. Seterusnya, kami menyemak newStr, nulldan jika ini berlaku, maka kami menukarnya kepada format rentetan kosong yang lebih mudah untuk kami - "". Kemudian kami mempunyai pengisytiharan pembolehubah yang kami perlukan:
  • jumlah panjang rentetan str;
  • panjang substring oldStr;
  • objek StringBuilderdaripada rentetan yang dikongsi.
Kami memulakan gelung yang sepatutnya berjalan beberapa kali bersamaan dengan panjang jumlah rentetan (tetapi, kemungkinan besar, ini tidak akan berlaku). Menggunakan kaedah kelas StringBuilder- indexOf- kita mengetahui indeks kejadian pertama subrentetan yang kita minati. Malangnya, saya ingin ambil perhatian bahawa ia indexOftidak berfungsi dengan ungkapan biasa, jadi kaedah terakhir kami hanya akan berfungsi dengan kejadian rentetan (( Jika indeks ini sama dengan -1, maka tiada lagi kejadian kejadian ini dalam objek semasa StringBuilder, jadi kita keluar daripada kaedah dengan hasil minat: ia terkandung dalam StringBuilder, yang kita tukar kepada String, menggunakan toString. Jika indeks kita sama -1dalam lelaran pertama gelung, maka subrentetan yang perlu diganti bukan dalam umum rentetan pada mulanya. Oleh itu, dalam keadaan sedemikian, kita hanya mengembalikan rentetan am. Seterusnya kita ada dan kaedah yang diterangkan di atas digunakan replaceuntuk StringBuildermenggunakan indeks kejadian yang ditemui untuk menunjukkan koordinat subrentetan yang akan diganti. Gelung ini akan dijalankan seberapa kerap subrentetan yang perlu diganti ditemui. Jika rentetan hanya terdiri daripada aksara yang perlu diganti, maka hanya dalam kes ini kita mempunyai Gelung akan berjalan sepenuhnya dan kita akan mendapat hasil yang StringBuilderditukar kepada rentetan. Kita perlu menyemak ketepatan kaedah ini, bukan? Mari tulis ujian yang menyemak operasi kaedah dalam pelbagai situasi:
@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, "");
}
Boleh dibahagikan kepada 7 ujian berasingan, setiap satunya akan bertanggungjawab untuk kes ujiannya sendiri. Setelah melancarkannya, kita akan melihat bahawa ia adalah hijau, iaitu, berjaya. Nah, nampaknya itu sahaja. Walaupun tunggu, kami berkata di atas bahawa kaedah ini akan lebih cepat replaceAlldaripada String. Baiklah, mari kita lihat:
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);
Seterusnya, kod ini dijalankan tiga kali dan kami mendapat keputusan berikut: Output konsol:
Performance ratio  - 5.012148941181627
 
Performance ratio  - 5.320637176017641
 
Performance ratio  - 4.719192686500394
Seperti yang kita dapat lihat, secara purata kaedah kami adalah 5 kali lebih produktif daripada replaceAllkelas klasik! StringNah, akhirnya, mari kita jalankan pemeriksaan yang sama, tetapi, boleh dikatakan, sia-sia. Dalam erti kata lain, dalam kes apabila tiada padanan ditemui. Mari gantikan rentetan carian dari "tyu"kepada "--". Tiga larian menghasilkan keputusan berikut: Output konsol:
Performance ratio  - 8.789647093542246
 
Performance ratio  - 9.177105482660881
 
Performance ratio  - 8.520964375227406
Secara purata, prestasi untuk kes yang tiada padanan ditemui meningkat sebanyak 8.8 kali! Gantikan rentetan dalam Java - 4
Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION