在替换之前检查字符串是不是包含子字符串是不是值得?

Posted

技术标签:

【中文标题】在替换之前检查字符串是不是包含子字符串是不是值得?【英文标题】:Is it worth it to check if a string contains a substring before replacing it?在替换之前检查字符串是否包含子字符串是否值得? 【发布时间】:2017-05-04 10:51:18 【问题描述】:

我今天看到了这段代码

if (translatedText.contains("â")) translatedText = translatedText.replace("â", "a");
if (translatedText.contains("ê")) translatedText = translatedText.replace("ê", "e");
...

这样的行有 22 行,我想知道在替换之前使用“ifs”有什么意义。我理解它的工作方式是,我们每行读取两次字符串,而直接调用 replace 方法在没有要替换的内容时会产生相同的效果,而在有要替换的内容时会更快。

但这只是我猜它的工作原理。有人可以确认或更正我吗?

还有第二个问题。我们正在对每个元音和每个符号“á”、“à”、“â”和“ä”进行替换。我敢打赌,在 Java 中有更好的方法来做到这一点。有什么建议吗?

谢谢。

【问题讨论】:

你为什么要这样做?我很确定它背后的要求是一个错误-我不是要评判什么,我只是相信我们可以在更深层次上帮助你 @Sebas 我想,这不是他的代码——他刚遇到它,想知道是否需要 如果代码中的操作是在条件检查之后执行的,那么它也执行得最好,否则它仍然运行。 对于ifcondition你需要查看你的数据长度和你的无效字符的频率,如果长度比较大那么我建议直接去替换 惊喜接受;但你是老板;-) 【参考方案1】:

documentation 并没有告诉我们如果没有匹配的子字符串,replace 会做什么,而是查看 Oracle 版本 (Java 8) 中的当前实现:

public String replace(CharSequence target, CharSequence replacement) 
    return Pattern.compile(target.toString(), Pattern.LITERAL).matcher(
            this).replaceAll(Matcher.quoteReplacement(replacement.toString()));

...如果您先检查,您确实可以避免一些工作,特别是内存分配(匹配器)。

这并不是说没有更好的方法来处理这 22 个替换,可能是通过使用带有字符类([âê] 等)的单个正则表达式,编译该正则表达式一次,并且然后在循环中使用单个匹配器,非常大致如下(灵感来自this answer):

// You can do this part once somewhere if you want to
Pattern regex = Pattern.compile("[âê]");
// Then:
StringBuffer resultString = new StringBuffer();
Matcher regexMatcher = regex.matcher(translatedText);
while (regexMatcher.find()) 
    String match = regexMatch.group();
    String replacement;
    switch (match) 
        // ...various cases setting `replacement`
    
    regexMatcher.appendReplacement(resultString, replacement);

regexMatcher.appendTail(resultString);
translatedText = resultString.toString();

或者如果您想过早地对其进行微优化(我的失败):

// You can do this part once somewhere if you want to
Pattern regex = Pattern.compile("[âê]");
// Then:
StringBuffer resultString = null;
Matcher regexMatcher = regex.matcher(translatedText);
while (regexMatcher.find()) 
    if (resultString == null) 
        resultString = new StringBuffer(translatedText.length() + 100);
    
    String match = regexMatch.group();
    String replacement;
    switch (match) 
        // ...various cases setting `replacement`
    
    regexMatcher.appendReplacement(resultString, replacement);

if (resultString != null) 
    regexMatcher.appendTail(resultString);
    translatedText = resultString.toString();

【讨论】:

恭喜。 500K 派对怎么样? @GhostCat: :-) 似乎只有你注意到了。 @GhostCat:好的,注意到了。 :-) (事实上,之前似乎总共发生了 12 次……然后是克劳德,幸运的 13 次!) 我在此保证:赚到 500K 后,我会简单地邀请整个 SO 参加庆祝派对……但考虑到今天投票的速度有多慢,嗯,嗯,那将需要一个同时。 @GhostCat 126k 似乎更近了,:)【参考方案2】:

关于“性能”:这可能真的取决于 JVM 版本;换句话说:取决于replace() 更改的实施,如果有,可以为您节省一些正则表达式匹配器成本;或不。

关于第二个问题:基本上你有很多重复的代码。一种解决方法:

你可以定义一些静态的 final 字段,比如:

Map<String, String> replacements = new HashMap<>();

然后填写:

replacements.put("â", "a");
...

然后用循环替换当前代码,该循环迭代该映射的条目,使用每个键/值作为 replace() 调用的参数。

或者,如另一个答案所示,您执行类似

的操作
replacements.put("[áàâä]", "a");

稍后使用 replaceAll()。

【讨论】:

【参考方案3】:

如果您希望摆脱明显多余的if 语句而不导致性能损失,那么快速的解决方案是改用replace(char, char)

translatedText = translatedText.replace('â', 'a');
translatedText = translatedText.replace('ê', 'e');

这完全避免了正则表达式,无论是显式的还是隐藏的,并且在我的 Java 8 中,如果没有替换,也可以避免创建一个新的 String

是否还有更好的方法取决于包括口味在内的几个因素。其他几个答案的想法很有希望。

【讨论】:

【参考方案4】:

您可以使用正则表达式将所有不需要的字符替换为您的字符

String s="sasaáàdaâadsasä";
System.out.println(s.replaceAll("[áàâä]", "a"));

输出:

sasaaadaaadsasa

[] 表示匹配其中任何出现的字符,如果找到则替换

要替换多个字符,您可以链接替换调用并简单地避免if 条件

String s="sasaáàdaâadsêêêasä";
String str=s.replaceAll("[áàâä]", "a").replaceAll("[ê]", "e");
System.out.println(str);

输出:

sasaaadaaadseeeasa

【讨论】:

他的用例也包括“ê”,因此您需要为每个字符进行映射。 对于每个“目标”-char @runDOSrun 的调用仍将减少到一个。 @Fildor 正确,但它没有提供ifs 的替代方案。 @runDOSrun 好吧,Pavneet 没有明确说明这一点,但我猜他会放弃ifs。

以上是关于在替换之前检查字符串是不是包含子字符串是不是值得?的主要内容,如果未能解决你的问题,请参考以下文章

检查字符串是不是包含模式(关于一对一的符号映射)

检查字符串是不是包含子字符串。此外,获取索引和匹配数(Raku)

检查字符串是不是包含 Velocity 中的特定子字符串

检查字符串是不是包含子字符串列表并保存匹配的子字符串

检查用户输入是不是包含子字符串

检查字符串是不是包含多个子字符串之一