提高Java中字符串连接的性能[重复]

Posted

技术标签:

【中文标题】提高Java中字符串连接的性能[重复]【英文标题】:Improving performance of string concatenation in Java [duplicate] 【发布时间】:2011-02-15 17:46:14 【问题描述】:

可能重复:java String concatenation

如何提高这段代码的性能:

public static String concatStrings(Vector strings) 
    String returnValue = "";

    Iterator iter = strings.iterator();
    while( iter.hasNext() ) 
        returnValue += (String)iter.next();
    

    return returnValue;

【问题讨论】:

这是作业吗?这本身并没有什么问题,它应该被标记为这样。 ***.com/questions/47605/java-string-concatenation 看起来不像一个典型的家庭作业问题。如果涉及性能,当然不是。学生通常没有意识到这一点,或者没有比较材料;) 不要使用原始类型,你可能不需要Vector的同步。 与 future-maintenance-performance 相关 - 您的方法可以采用 Iterable<String> 代替。 【参考方案1】:

您可能会考虑使用StringBuilder,而不是对单个字符串使用+=。字符串在 Java 中是不可变的,这意味着一旦创建了 String 对象,就无法修改它。在循环中对字符串使用 += 将导致创建许多单独的 String 实例,这可能会产生性能问题。 StringBuilder 可以连接字符串,而无需创建新实例,这可能会节省一些时间,具体取决于具体场景。

【讨论】:

+= 每次都会隐式创建一个 new 字符串。 你能把你的解释写成代码吗? 循环前创建StringBuilder,循环内调用append()方法,循环后使用toString()得到结果。 我不能把它放在代码中,但它很容易。只需创建一个新的 StringBuilder,并在循环中使用“append”方法将新字符串添加到缓冲区。完成后,只需调用 stringBuilder.toString() 即可获得最终结果。【参考方案2】:
public static String concatStrings(List<String> strings) 
    StringBuilder sb = new StringBuilder();
    for (String s : strings) 
       sb.append(s);
        
    return sb.toString();

一些备注:

在需要在循环中构建字符串时使用StringBuilder + 适用于简单的连接,但对于增量构建来说很糟糕 尽可能使用for-each 以提高可读性 java.util.Vectorsynchronized;如果您不需要这个(昂贵的)功能,只需使用ArrayList

不要使用原始类型

JLS 4.8 Raw Types

仅允许使用原始类型作为对遗留代码兼容性的让步。强烈反对在将泛型引入 Java 编程语言之后编写的代码中使用原始类型。 Java 编程语言的未来版本可能会禁止使用原始类型。

有效的 Java 第 2 版:第 23 条:不要在新代码中使用原始类型

如果您使用原始类型,您将失去泛型的所有安全性和表现力优势。

另见

Java string concatenation Java Tutorials - Generics

【讨论】:

另见***.com/questions/2770321/…【参考方案3】:

正如其他答案所建议的那样,使用StringBuilder 可能是更好的选择。

问题中给出的代码实际上将被编译(使用 Sun 的 javac)为以下内容:

public static String concatStrings(Vector strings) 
    String returnValue = "";

    Iterator iter = strings.iterator();
    while( iter.hasNext() ) 
        String str = (String)iter.next();

        StringBuilder sb = new StringBuilder(returnValue);
        sb.append(str);

        returnValue = sb.toString();
    

    return returnValue;

编译器会将+= 字符串连接更改为使用StringBuilder 的连接。但是,编译器可能会重写循环内的代码,因此每次迭代都会创建一个新的StringBuilder 实例,这对性能不太友好。

因此,在这种情况下,我们自己在循环外创建一个StringBuilder 并执行手动字符串连接可能会更好:

public static String concatStrings(Vector strings) 
    StringBuidler returnValueBuilder;

    Iterator iter = strings.iterator();
    while( iter.hasNext() ) 
        returnValueBuilder.append((String)iter.next());
    

    return returnValueBuilder.toString();

【讨论】:

+1 因为编译器会在某些情况下优化连接,即“+”并不总是邪恶的。但是,在问题中提出的那种情况下,开销不是创建新的 StringBuilder 实例(对象分配通常非常快),而是在返回新 String 之前将 StringBuilder 缓冲区复制到 String (String guarantees immutability so they can't重复使用相同的缓冲区) @CurtainDog - IIRC,StringBuilder.toString() 在创建结果String 时巧妙地避免了复制字符数组。看一下代码。 @Stephen C:最新版本绝对不是这样。一般来说,Java 标准库会非常小心地避免任何可能的情况,即字符串可能会被意外变异。见google.com/codesearch/p?hl=en#gbgUBv1WUj4/src/share/classes/… @David - 我今天学到了一些新东西!显然变化发生在 1.4.x 和 1.5 之间……bugs.sun.com/bugdatabase/view_bug.do?bug_id=6219959 @Stephen C - 谢谢,你让我觉得自己很年轻……我所有严肃的 Java 编码都必须在 1.5 之后出现。当然,我发现我最初的评论是不准确的,因为“无法重用”改为“发现重用有问题”【参考方案4】:
private static final int AVERAGE_STRING_LENGTH = 10;  // Using 10 is arbitrary

public static final String concatStrings(final Collection<String> strings) 
    if (strings == null)   return null;

    final int size = strings.size();
    if (size == 0)         return "";
    if (size == 1)         return strings.get(0);

    final StringBuilder returnValue =
        new StringBuilder(AVERAGE_STRING_LENGTH * size);

    for (String s : strings) 
        returnValue.append(s);
    

    return returnValue.toString();

也许有点过火了,以下是针对concatStrings()我能想到的所有优化 - 如上所示 - 其中一些可能不适用于您的环境:

使用StringBuilder - 对于这些连续的连接,它的效率要高得多 使用StringBuilder(int capacity) 指定可能需要的容量,如果有任何方法可以预测的话(上面使用了平均大小,但其他方法可能更方便) 使用Collection 参数类型允许比Vector 更有效的数据结构,后者是同步的 - 加上调用者具有更大的灵活性(例如,无需将Set&lt;String&gt; 复制到Vector&lt;String&gt; 只需调用此方法) 硬编码简单案例,如果可能的话(例如,null、大小 0 和大小 1 以上案例) 使用final 促进JIT 内联和优化 缓存strings的大小,如果它被多次使用。 (例如,在上面的代码中使用了 3 次。)

最后,如果此操作经常在大量字符串上执行,请查看Ropes for Java

Java 文章的绳索 - http://www.ibm.com/developerworks/java/library/j-ropes Java 实现的绳索 - http://ahmadsoft.org/ropes/ 绳索(***)-http://en.wikipedia.org/wiki/Rope_(computer_science)

【讨论】:

【参考方案5】:

此外,如果您希望加快速度,您可以重构代码以使用 ArrayList 而不是 Vector。 ArrayList 不是线程安全的,所以比 Vector 稍快(视情况而定,可能是 0% 的差异,也可能是 5% 的差异)。

【讨论】:

【参考方案6】:

每次调用 += 时都会创建一个字符串。例如

String theString = "1"; //Makes an immutable String object "1"
theString +="2"; //Makes a new immutable String object "12"
theString +="3"; //makes a new immutable String object "123"

使用字符串生成器可以避免这个问题。

StringBuilder sb = new StringBuilder("1"); //Makes a StringBuilder object holding 1
sb.append("2"); //The same StringBuilder object now has "12" in it.
sb.append("3"); //The same StringBuidler object now has "123" in it. 
String theString = sb.toString(); //Creates a new String object with "123" in it 

请注意,在第一个示例中,我们如何创建所有这些中间字符串,而在第二个示例中,我们只创建了 StringBuilder 和最终字符串(在两个示例中,我们在使用它们时创建了“1”、“2”和“3”作为论据)。您可以看到在第一个示例中创建的对象较少,如果您对 String 进行大量附加操作,您可以想象加起来的效果!

【讨论】:

【参考方案7】:

除了使用 StringBuilder 之外,您还可以预先遍历 String 列表并计算 StringBuilder 所需的确切大小。然后将此值传递给 StringBuilder 构造函数。请注意,这将属于过早优化的类别,但您确实要求性能...(您应该查看用于增加 StringBuilder/StringBuffer 缓冲区的代码,其教育意义)

【讨论】:

【参考方案8】:

除了使用 ArrayList 和 StringBuilder 之外,让我们考虑一下。

在现代计算机科学范式中,空间几乎总是可以换取时间(也许,这是一种主观陈述)。对于给定的场景,使用下面的代码,使用了 O(N) 的额外空间,其中 N = 没有字符串(对于保存 list.toArray() 的新缓冲区)。这至少比使用 Iterator 好(打开 AbstractList.iterator())。重要的是,通过在一次迭代中一次计算两个字符串的连接,时间复杂度明显更好,从而将迭代次数减少了一半!这有点像使用动态编程方法(记住,使用动态编程计算斐波那契数)!!

    StringBuilder sb = new StringBuilder();
    Object[] o = list.toArray();
    //For even no of Strings
    if(o.length % 2 == 0)
        concatFaster(sb, o);
     else 
        //For odd no of Strings
        concatFaster(sb, o);
        sb.append(o[o.length-1]); // For the odd index
    

    public static void concatFaster(StringBuilder sb, Object[] o) 
    for (int i = 0; i < o.length - 1; i+=2) 
        sb.append(o[i]).append(o[i+1]);
    

【讨论】:

以上是关于提高Java中字符串连接的性能[重复]的主要内容,如果未能解决你的问题,请参考以下文章

性能(JAVA)~ 循环中的字符串连接,带有前置和附加

连接运算符 (+) 与 concat() [重复]

在循环中连接 Java 数组 [重复]

C ++和Java中的字符串连接复杂性[重复]

php 字符串连接,性能

高性能Java代码的规范