在程式設計師的工作中,經常會重複某些任務或其元件。因此,今天我想談談 Java 開發人員日常工作中經常遇到的一個主題。 假設您從某個方法收到某個字串。而且一切看起來都不錯,但有一些小地方不太適合你。例如,分隔符號不合適,您需要其他分隔符號(或根本不需要)。在這種情況下可以做些什麼呢?
replace
自然地,使用類別的方法String
。
Java字串替換
類型物件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
正如我們所看到的,只有「Diego」的第一個出現發生了變化,但後續的出現仍然被遺漏——也就是說,沒有受到影響。 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]
— 描述從 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 個拉丁字元組成且周圍有空格的單字。因此,該模板將替換為我們傳遞的字串。
Java正規表示式替換
基本上,要在 Java 中使用正規表示式,java.util.regex
. 關鍵類別是:
Pattern
- 提供正規表示式的編譯版本的類別。Matcher
— 此類解釋模式並決定其接收的字串中的符合項目。
Matcher
那麼,在and的幫助下,我們之前的對象會是什麼樣子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
事實,即它在創建後就無法更改。因此,當我們使用方法替換字串的某些部分時,我們不會更改對象,而是每次創建一個新對象,並包含必要的內容。但是每次創建一個新物件需要很長時間,不是嗎?特別是當問題不是幾個對象,而是數百個甚至數千個時。無論你願意與否,你開始考慮替代方案。我們還有什麼選擇?嗯...說到它的屬性,你立刻就會想到替代方案,但不是,即StringBuilder/StringBuffer。正如我們所記得的,這些類別實際上並沒有什麼不同,只是它們針對多執行緒環境中的使用進行了最佳化,因此它們在單執行緒使用中運行得更快一些。基於此,今天我們將使用 這個類別有很多有趣的方法,但具體來說現在我們感興趣的是。 — 此方法將此序列的子字串中的字元替換為指定字串中的字元。子字串從指定的開頭開始,一直持續到索引末尾的字符,或者如果不存在這樣的字符,則一直到序列的末尾。讓我們來看一個例子: String
immutable
replace
String
String
immutable
immutable
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
,因此,我們帶著感興趣的結果退出該方法:它包含在our 中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);
接下來,程式碼運行了 3 次,我們得到了以下結果: 控制台輸出:
Performance ratio - 5.012148941181627
Performance ratio - 5.320637176017641
Performance ratio - 4.719192686500394
正如我們所看到的,我們的方法平均效率是經典replaceAll
類別的String
5 倍!好吧,最後,讓我們進行同樣的檢查,但是,可以說,是徒勞的。換句話說,在沒有找到匹配項的情況下。讓我們將搜尋字串從 替換"tyu"
為"--"
。三次運行產生以下結果: 控制台輸出:
Performance ratio - 8.789647093542246
Performance ratio - 9.177105482660881
Performance ratio - 8.520964375227406
平均而言,未找到匹配項的情況下的效能提高了 8.8 倍!
還有什麼要讀的: |
---|
GO TO FULL VERSION