String.trim()比String.replace()更快吗?

9
假设有一个字符串,例如" world "。该字符串只有前后空格。在这种情况下,trim()replace() 更快吗?
我曾经使用过replace(),但我的导师说不要使用它,因为trim() 可能更快。
如果不是,trim()replace() 有什么优势?

11
它们的工作方式不同。trim仅在开头和结尾起作用,而replace会替换字符串中间的字符。 - markspace
2
这非常依赖于具体实现。 - Mad Physicist
1
同样的,我会像你的导师一样做出相同的建议,但不是因为速度的问题。这是因为替换操作更加危险,因为有一天你可能会得到一个字符串,其中包含不仅仅在末尾周围的空格。 - Mad Physicist
1
@shmosel。最好附上解释。 - Mad Physicist
3
它们执行不同的操作并传达不同的意图。我不是很明白这个问题。如果没有,trim()相对于replace()有什么优势? - shmosel
显示剩余6条评论
5个回答

13
如果我们查看方法的源代码,请看此处replace()方法:
 public String replace(CharSequence target, CharSequence replacement) {
    String tgtStr = target.toString();
    String replStr = replacement.toString();
    int j = indexOf(tgtStr);
    if (j < 0) {
        return this;
    }
    int tgtLen = tgtStr.length();
    int tgtLen1 = Math.max(tgtLen, 1);
    int thisLen = length();
    int newLenHint = thisLen - tgtLen + replStr.length();
    if (newLenHint < 0) {
        throw new OutOfMemoryError();
    }
    StringBuilder sb = new StringBuilder(newLenHint);
    int i = 0;
    do {
        sb.append(this, i, j).append(replStr);
        i = j + tgtLen;
    } while (j < thisLen && (j = indexOf(tgtStr, j + tgtLen1)) > 0);
    return sb.append(this, i, thisLen).toString()
}

trim()相比:

public String trim() {
    int len = value.length;
    int st = 0;
    char[] val = value;    /* avoid getfield opcode */
    while ((st < len) && (val[st] <= ' ')) {
        st++;
    }
    while ((st < len) && (val[len - 1] <= ' ')) {
        len--;
    }
    return ((st > 0) || (len < value.length)) ? substring(st, len) : this;
}

正如您所看到的,replace() 调用多个其他方法并在整个 String 中进行迭代,而 trim() 仅在 String 的开头和结尾进行迭代,直到字符不是空格。因此,在仅尝试删除单词前后的空格时,trim() 更有效率。


我们可以对此运行一些基准测试:

public static void main(String[] args) {
       long testStartTime = System.nanoTime();;
       trimTest();
       long trimTestTime = System.nanoTime() - testStartTime;
       testStartTime = System.nanoTime();     
       replaceTest();
       long replaceTime = System.nanoTime() - testStartTime;           
       System.out.println("Time for trim(): " + trimTestTime);
       System.out.println("Time for replace(): " + replaceTime);            
}

public static void trimTest() {
    for(int i = 0; i < 1000000; i ++) {     
        new String("  string   ").trim();
    }
}
public static void replaceTest() {
    for(int i = 0; i < 1000000; i ++) {     
        new String("  string   ").replace(" ", "");
    }
}

输出:

Time for trim(): 53303903
Time for replace(): 485536597
//432,232,694 difference

我本来会怀疑这样的情况,但是replace中的调用次数确实比我预期的要多得多。不过,这取决于具体实现。我认为规则是在适用trim的情况下使用它,在需要时使用replace。只有当运行真实代码的分析器说某个部分需要优化时才进行优化。 - markspace

4
假设编写Java库代码的人员做得很好1,那么您可以认为特定用途的方法(如trim())将与执行相同操作的通用方法(如replace(...))一样快,甚至更快。
原因有两个:
  • 如果特定用途的方法较慢,则其实现可以重新编写为等效调用通用方法,从而在大多数情况下性能相当。称职的程序员会这样做,因为它可以降低维护成本。

  • 在特定用途的方法中,可能存在无法应用于通用情况的优化。

在这种情况下,我们知道trim()只需要查看字符串的开头和结尾...而replace(...)需要查看字符串中的所有字符。(我们可以从各自方法的描述中推断出这一点。)
如果我们假设“称职”,那么我们可以推断开发人员已经进行了分析,并且没有将trim()实现为检查所有字符的子优化2
使用特定用途的方法而不是通用方法还有另一个原因。这使您的代码更简单、易读和易于检查正确性。这可能比性能更重要。
这在trim()replace(...)的情况下显然适用。
1 - 在这种情况下,我们可以这样做。有很多人看着这段代码,并且很多人会大声抱怨性能问题。 2 - 不幸的是,事情并不总是这么简单。库方法需要针对“典型”行为进行优化,但它还需要避免在边缘情况下出现病态性能。并不总是可能同时实现这两个目标。

3

trim()是一种更快捷的输入方式,它不需要任何参数。

同时理解你的意图也更加迅速明了。你想对字符串进行修剪,而不是替换其中所有包含空格的部分为空字符串,并且在其他情境中知道该字符串仅在开头和结尾有空格

从任何角度看,使用trim()都比较快速。不要让阅读你代码的人生活变得复杂。大多数情况下,这个人将会是你几个月后,或至少是你不讨厌的某个人。


1

Trim将修剪外部字符,直到它们不是空格。我认为它们修剪空格、制表符和换行符。

Replace将扫描整个字符串(因此,它可以是一个句子),并用“”替换内部的“”,从而将它们压缩在一起。

它们有不同的用例,显然,一个是清理用户输入,而另一个是在找到匹配项后使用其他内容更新字符串。

话虽如此,运行时间:替换将在N时间内运行,因为它将查找所有匹配的字符。Trim将在O(N)中运行,但很可能只是每端少量字符。

我认为,修剪背后的想法是人们会在键入和输入事物时意外地在提交表单之前按下空格,从而尝试保存字段“Foo ”而不是“Foo”。


0

s.trim() 缩短了一个 String s。这意味着不需要将字符从一个索引移动到另一个索引。它从 String 的第一个字符 (s.toCharArray()[0]) 开始,逐个字符缩短 String,直到出现第一个非空格字符。它以相同的方式缩短字符串末尾的 String。因此,它压缩了 String。如果一个 String 没有前导和尾随空格,则在检查第一个和最后一个字符后,trim 将准备就绪。

对于 " world ".trim(),需要两个步骤:一个是删除第一个索引上的前导空格,第二个是删除最后一个索引上的尾随空格。

" world ".replace(" ", "") 至少需要 n = " world ".length() 步。它必须检查每个字符是否需要替换。但是,如果我们考虑到 String.replace(...) 的实现需要编译一个 Pattern,构建一个 Matcher,然后替换所有匹配的区域,与缩短一个 String 相比,它似乎更加复杂。

我们还必须考虑到 " world ".replace(" ", "") 不会替换空格,而只会替换字符串 " "。由于 String replace(CharSequence target, CharSequence replacement) 使用 Pattern.LITERAL 编译目标,因此我们无法使用字符类 \s。为了更准确,我们应该将 " world ".trim()" world ".replaceAll("\\s", "") 进行比较。这仍然不同,因为在 String trim() 中,空格被定义为对于 s.toCharArray() 中的每个 cc <= ' '

总结:对于长字符串来说,String.trim() 应该更快。

关于方法如何工作的描述是基于Java 8中String的实现。但是实现可能会改变。

但问题应该是:您打算如何处理字符串?您想要修剪它还是替换一些字符?根据使用相应的方法进行操作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接