如何提高此代码块的性能:
public static String concatStrings(Vector strings) {
String returnValue = "";
Iterator iter = strings.iterator();
while( iter.hasNext() ) {
returnValue += (String)iter.next();
}
return returnValue;
}
答案 0 :(得分:14)
您可能会考虑使用StringBuilder,而不是使用单个字符串执行+ =。字符串在Java中是不可变的,这意味着一旦创建了String对象,就无法对其进行修改。在循环中对字符串使用+ =将导致创建许多单独的String实例,这可能会产生性能问题。 StringBuilder可以连接字符串而无需创建新实例,这可能会节省一些时间,具体取决于具体情况。
答案 1 :(得分:8)
public static String concatStrings(List<String> strings) {
StringBuilder sb = new StringBuilder();
for (String s : strings) {
sb.append(s);
}
return sb.toString();
}
一些评论:
StringBuilder
+
适用于简单连接,但增量构建很糟糕java.util.Vector
是synchronized
;如果您不需要这个(昂贵的)功能,只需使用ArrayList
。原始类型的使用仅允许作为遗留代码兼容性的让步。强烈建议不要在将通用性引入Java编程语言之后编写的代码中使用原始类型。 未来版本的Java编程语言可能会禁止使用原始类型。
Effective Java 2nd Edition:第23项:不要在新代码中使用原始类型
如果您使用原始类型,则会失去仿制药的所有安全性和表现力。
答案 2 :(得分:6)
正如其他答案所示,使用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();
}
答案 3 :(得分: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<String>
到Vector<String>
只是为了调用这种方法)null
,尺寸0
和尺寸1
以上情况final
促进JIT内联和优化strings
的大小。 (例如,在上面的代码中使用了3次。)最后,如果此操作经常在大量字符串上完成,请查看Ropes for Java。
答案 4 :(得分:1)
此外,如果您希望加快速度,可以重构代码以使用ArrayList而不是Vector。 ArrayList不是线程安全的,所以它比Vector稍快(取决于情况,可能是0%的差异,可能是5%的差异)。
答案 5 :(得分:1)
每次调用+ =时都会创建一个字符串。例如
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进行大量附加,您可以想象它是如何累加的!
答案 6 :(得分:1)
除了使用StringBuilder之外,您还可以预先遍历字符串列表并计算StringBuilder所需的确切大小。然后将此值传递给StringBuilder构造函数。请注意,这将属于过早优化的类别,但您确实要求性能......(您应该查看用于增长StringBuilder / StringBuffer缓冲区的代码,其教育性)
答案 7 :(得分:0)
除了使用ArrayList和StringBuilder之外,让我们考虑一下。
在现代计算机科学范式中,空间几乎总是可以随时间交换(也许,这是一种主观陈述)。对于给定的场景,使用下面的代码,使用额外的O(N)空间,其中N =没有字符串(对于包含list.toArray()的新缓冲区)。这比至少使用Iterator更好,(打开AbstractList.iterator())。重要的是,通过在一次迭代中一次计算两个字符串的串联,时间复杂度明显更好,从而将迭代次数减少一半!这类似于使用动态编程方法(记住,使用动态编程计算Fibonacci nos !!)
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]);
}
}