Pie*_*igi 97 java string performance stringbuilder
我有关于使用StringBuilder的性能相关问题.在一个很长的循环中,我正在操纵a StringBuilder并将其传递给另一个方法,如下所示:
for (loop condition) {
StringBuilder sb = new StringBuilder();
sb.append("some string");
. . .
sb.append(anotherString);
. . .
passToMethod(sb.toString());
}
Run Code Online (Sandbox Code Playgroud)
StringBuilder在每个循环周期实例化是一个很好的解决方案吗?并且更好地调用删除,如下所示?
StringBuilder sb = new StringBuilder();
for (loop condition) {
sb.delete(0, sb.length);
sb.append("some string");
. . .
sb.append(anotherString);
. . .
passToMethod(sb.toString());
}
Run Code Online (Sandbox Code Playgroud)
Epa*_*aga 67
第二个在我的迷你基准测试中快了大约25%.
public class ScratchPad {
static String a;
public static void main( String[] args ) throws Exception {
long time = System.currentTimeMillis();
for( int i = 0; i < 10000000; i++ ) {
StringBuilder sb = new StringBuilder();
sb.append( "someString" );
sb.append( "someString2"+i );
sb.append( "someStrin4g"+i );
sb.append( "someStr5ing"+i );
sb.append( "someSt7ring"+i );
a = sb.toString();
}
System.out.println( System.currentTimeMillis()-time );
time = System.currentTimeMillis();
StringBuilder sb = new StringBuilder();
for( int i = 0; i < 10000000; i++ ) {
sb.delete( 0, sb.length() );
sb.append( "someString" );
sb.append( "someString2"+i );
sb.append( "someStrin4g"+i );
sb.append( "someStr5ing"+i );
sb.append( "someSt7ring"+i );
a = sb.toString();
}
System.out.println( System.currentTimeMillis()-time );
}
}
Run Code Online (Sandbox Code Playgroud)
结果:
25265
17969
Run Code Online (Sandbox Code Playgroud)
请注意,这是使用JRE 1.6.0_07.
基于Jon Skeet在编辑中的想法,这里是版本2.但是结果相同.
public class ScratchPad {
static String a;
public static void main( String[] args ) throws Exception {
long time = System.currentTimeMillis();
StringBuilder sb = new StringBuilder();
for( int i = 0; i < 10000000; i++ ) {
sb.delete( 0, sb.length() );
sb.append( "someString" );
sb.append( "someString2" );
sb.append( "someStrin4g" );
sb.append( "someStr5ing" );
sb.append( "someSt7ring" );
a = sb.toString();
}
System.out.println( System.currentTimeMillis()-time );
time = System.currentTimeMillis();
for( int i = 0; i < 10000000; i++ ) {
StringBuilder sb2 = new StringBuilder();
sb2.append( "someString" );
sb2.append( "someString2" );
sb2.append( "someStrin4g" );
sb2.append( "someStr5ing" );
sb2.append( "someSt7ring" );
a = sb2.toString();
}
System.out.println( System.currentTimeMillis()-time );
}
}
Run Code Online (Sandbox Code Playgroud)
结果:
5016
7516
Run Code Online (Sandbox Code Playgroud)
Pet*_*ter 25
在编写可靠代码的哲学中,将StringBuilder放在循环中总是更好.这样它就不会超出其预期的代码范围.
其次,StringBuilder中最大的改进来自给它一个初始大小,以避免它在循环运行时变大
for (loop condition) {
StringBuilder sb = new StringBuilder(4096);
}
Run Code Online (Sandbox Code Playgroud)
Dav*_*vis 23
更快:
public class ScratchPad {
private static String a;
public static void main( String[] args ) throws Exception {
long time = System.currentTimeMillis();
StringBuilder sb = new StringBuilder( 128 );
for( int i = 0; i < 10000000; i++ ) {
// Resetting the string is faster than creating a new object.
// Since this is a critical loop, every instruction counts.
//
sb.setLength( 0 );
sb.append( "someString" );
sb.append( "someString2" );
sb.append( "someStrin4g" );
sb.append( "someStr5ing" );
sb.append( "someSt7ring" );
setA( sb.toString() );
}
System.out.println( System.currentTimeMillis()-time );
}
private static void setA( String aString ) {
a = aString;
}
}
Run Code Online (Sandbox Code Playgroud)
在编写固体代码的哲学中,应该从使用该方法的对象中隐藏该方法的内部工作方式.因此,无论您是在循环内还是循环外重新声明StringBuilder,系统的视角都没有区别.由于在循环之外声明它更快,并且它不会使代码更复杂,因此重用该对象而不是重新实例化它.
即使代码更复杂,并且您确定对象实例化是瓶颈,请对其进行评论.
这个答案有三次运行:
$ java ScratchPad
1567
$ java ScratchPad
1569
$ java ScratchPad
1570
Run Code Online (Sandbox Code Playgroud)
另外三个回答:
$ java ScratchPad2
1663
2231
$ java ScratchPad2
1656
2233
$ java ScratchPad2
1658
2242
Run Code Online (Sandbox Code Playgroud)
虽然不重要,但设置StringBuilder初始缓冲区大小会产生很小的增益.
Jon*_*eet 12
好的,我现在明白发生了什么,它确实有意义.
我的印象是toString刚刚将底层传递给char[]了一个没有复制的String构造函数.然后将在下一次"写入"操作(例如delete)上进行复制.我相信在以前的版本中就是这种情况StringBuffer.(现在不是.)但是没有 - toString只是将数组(以及索引和长度)传递给String需要复制的公共构造函数.
因此,在"重用StringBuilder"的情况下,我们真正为每个字符串创建一个数据副本,在整个缓冲区中使用相同的char数组.显然StringBuilder每次创建一个新的创建一个新的底层缓冲区 - 然后在创建一个新字符串时复制该缓冲区(在我们的特定情况下,有些无意义,但出于安全原因).
所有这些导致第二个版本肯定更高效 - 但同时我仍然说它是更丑陋的代码.
由于我不认为它已被指出,因为Sun Java编译器内置了优化,当它看到String连接时会自动创建StringBuilders(StringBuffers pre-J2SE 5.0),问题中的第一个示例相当于:
for (loop condition) {
String s = "some string";
. . .
s += anotherString;
. . .
passToMethod(s);
}
Run Code Online (Sandbox Code Playgroud)
哪个更具可读性,IMO,更好的方法.您尝试优化可能会导致某些平台获益,但可能会损失其他平台.
但如果你真的遇到性能问题,那么肯定,优化.我首先明确指定StringBuilder的缓冲区大小,每个Jon Skeet.
| 归档时间: |
|
| 查看次数: |
65503 次 |
| 最近记录: |