我有一段旧代码,用于查找和替换字符串中的标记。
它接收from和to对的映射,对其进行迭代,并针对每个对,迭代目标字符串,使用indexOf()查找from,并将其替换为。它在StringBuffer上完成所有工作,并最终返回String。
我用以下代码替换了该代码:replaceAll("[,. ]*","");
我进行了一些比较性能测试。
比较1,000,000迭代时,我得到了:
Old Code: 1287ms
New Code: 4605ms
长3倍!
然后,我尝试用3个对replace的调用替换它:
replace(",","");
replace(".","");
replace("","");
结果如下:
Old Code: 1295
New Code: 3524
长2倍!
知道为什么replace和replaceAll如此低效吗?我可以做些什么使它更快吗?
编辑:感谢所有的答案-主要的问题确实是[,. ]*没有执行我想要的操作。将其更改为[,. ]+几乎等于基于非Regex的解决方案的性能。
使用预编译的正则表达式虽然有所帮助,但却是微不足道的。 (这是一个非常适合我的问题的解决方案。
测试代码:
用正则表达式替换字符串:[,。 ] *
用正则表达式替换字符串:[,。 ] +
用正则表达式替换字符串:[,。 ] +和预编译模式
尽管使用正则表达式会给性能带来一些影响,但它不应该那么糟糕。
注意,每次调用String.replaceAll()时,都会编译正则表达式。
您可以通过显式使用Pattern对象来避免这种情况:
1 2 3 4
| Pattern p = Pattern. compile("[,. ]+");
// repeat only the following part:
String output = p. matcher(input ). replaceAll(""); |
还要注意,使用+而不是*可以避免替换空字符串,因此也可以加快处理速度。
-
@Lukas:它不会更改所做的操作(在这种情况下,替换为空字符串)。它可能会更改性能特征,但我不知道忽略+是更快还是更慢。
-
有趣的是,@ Joachim对您的解决方案进行了简单的基准测试,[]+似乎最快(1x),[]在中间(1.5x),而[]*最慢(2x)。
-
该答案假定他收到的地图一再重复使用。
-
@Joachim,[] *匹配空字符串"。因此,对于每个字符,都会找到匹配项",并替换为"。这会使它变慢。您可以通过执行.... replaceAll(" $ $$$"),然后观察输出。
-
@ SJuan76:我很清楚这一点(我已经在回答中对此进行了解释)。我唯一不知道的是replaceAll("[,. ]+","")会比replaceAll("[,. ]","")慢还是快。
replace和replaceAll在内部使用正则表达式,与例如StringUtils.replace(..)相比,在大多数情况下会对性能产生严重影响。
String.replaceAll():
1 2 3 4
| public String replaceAll (String regex, String replacement ) {
return Pattern. compile(regex ). matcher(this ). replaceAll(
replacement );
} |
String.replace()在下面使用Pattern.compile。
1 2 3 4 5
| public String replace (CharSequence target, CharSequence replacement ) {
return Pattern. compile(target. toString(), Pattern. LITERAL)
. matcher(this ). replaceAll(
Matcher. quoteReplacement(replacement. toString()));
} |
另请参阅替换字符串中所有出现的子字符串-在Java中更有效吗?
-
replace()不使用正则表达式。
-
@Adeel,String.replace(char, char)没有。 String.replace(CharSequence, CharSequence)可以。
-
@Lukas:这些都不是。顺便说一句,replace(char, char)毫无疑问,因为它不能使用正则表达式,而OP从未使用过它。
-
@Lukas String.replace(CharSequence, CharSequence)使用正确的编译模式,但未针对正则表达式进行编译。
-
@Sean是+1,尽管从技术上说abc表达式仍然是有效的正则表达式。
-
@Johan是,这也是儿童歌曲的前三个字。 (这也无关紧要)
正如我发表的评论[,。 ] *匹配空字符串""。因此,字符之间的每个"空格"都与模式匹配。仅在性能中会指出这一点,因为您要替换很多""。
尝试这样做:
1 2
| Pattern p = Pattern. compile("[,. ]*");
System. out. println(p. matcher("Hello World"). replaceAll("$$$"); |
它返回:
H $$ e $$$$$$ o $$$$$ W $$ o $$ r $$$ l $$$ d $$!$$$
难怪"手工"做起来会比较慢!您应该尝试使用[,。 ] +
当涉及到replaceAll("[,. ]*","")时,它并不依赖于正则表达式,因此并不令人惊讶。正则表达式引擎创建一个自动机,并在输入上运行。预计会有一些开销。
第二种方法(replace(",","")...)在内部也使用正则表达式。但是,此处给定的模式使用Pattern.LITERAL进行编译,因此正则表达式的开销可以忽略不计。)在这种情况下,可能是由于Strings是不可变的(无论您进行的很小的更改,您都会创建一个新字符串) ),因此效率不如StringBuffers来就地操纵字符串。
-
@aiiobe:String.replace(CharSequence, CharSequence)在内部使用正则表达式...
-
是的,但是使用Pattern.LITERAL实际上会关闭所有正则表达式开销。
-
但这恰恰意味着它没有,我只能将正则表达式传递给它,但它不能按预期工作。
-
不过,LITERAL模式的编译不应该超级复杂,是吗? ;-)