在程序员的工作中,经常会重复某些任务或其组件。因此,今天我想谈谈 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