假设我需要启动一个String然后再更新它几次,就像在这个伪代码中一样:
String s = "first";
if (<some condition>) { s += "second"; }
if (<some condition>) { s += "third"; }
if (<some condition>) { s += "fourth"; }
如果我最多有4次,那么以这种方式使用它还是只使用StringBuffer / StringBuilder会更好吗?
实际上我要问的是,如果String(这是一个可变类型)的更新时间是使用上述选项的有效方式吗?
答案 0 :(得分:4)
首先,String不是一个可变类。每次使用+=
时,都会返回另一个对象。
其次,除非你在紧密循环中使用它,否则无论如何都不是很重要。
第三,虽然它可能不是类似计算机科学的方法,但如果我尝试使用
StringBuilder s = new StringBuilder("first");
s.append( "second" );
s.append( "third" );
s.append( "fourth" );
Intellij建议用String替换它,并且只有在使用简单的String时至少同样有效才会出现该建议(尽管由于条件可能不完全相同)
根据Java: String concat vs StringBuilder - optimised, so what should I do? Java编译器确实用String构建器替换了一系列String连接:
public static void main(String[] args) {
String s = "first";
s+=" second" ;
if(args.length >0)
s+=" third";
System.out.println("s, = " + s);
}
使用JDK 1.8.0_66并使用javap -c SbTest.class
生成以下输出。虽然我不是字节代码的专家,但似乎创建了StringBuilder
的多个实例(3
,28
)。因此,正如链接答案中所建议的那样,编译后的代码似乎是这样的:
String s = "first";
s = new StringBuilder().append(s).append(" second") ;
if(args.length >0)
s = new StringBuilder().append(s).append(" third") ;
因此,除非JIT对此进行优化,否则自己使用StringBuilder
可能仍然更有效。
public static void main(java.lang.String[]);
Code:
0: ldc #2 // String first
2: astore_1
3: new #3 // class java/lang/StringBuilder
6: dup
7: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
10: aload_1
11: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
14: ldc #6 // String second
16: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
22: astore_1
23: aload_0
24: arraylength
25: ifle 48
28: new #3 // class java/lang/StringBuilder
31: dup
32: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
35: aload_1
36: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
39: ldc #8 // String third
41: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
44: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
47: astore_1
48: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream;
51: new #3 // class java/lang/StringBuilder
54: dup
55: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
58: ldc #10 // String s, =
60: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
63: aload_1
64: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
67: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
70: invokevirtual #11 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
73: return
答案 1 :(得分:2)
String s = "first";
if (<some condition>) { s += "second"; }
if (<some condition>) { s += "third"; }
if (<some condition>) { s += "fourth"; }
是正确的方法,因为编译器将turn it into a StringBuilder ( JDK 1.6及以上) 也就是说,除非您在循环中使用它,否则优化是相同的。
有关详细信息,请参阅此link
答案 2 :(得分:0)
如果你正在使用合理大小的字符串(比如你在那里的单词),使用字符串类就完全没问题了,使用字符串构建器类比使用普通字符串要快得多。当你可能处理大字符串(想想许多段落)并反复更改它们时,你想要替换它。 StringBuilder和StringBuffer应该大致相同,但StringBuffer是线程安全的,所以如果要编写多线程代码,那么使用StringBuffer。
答案 3 :(得分:-1)
要知道的唯一方法是进行基准测试。
package com.example;
import static org.junit.Assert.*;
import java.time.Duration;
import java.time.Instant;
import org.junit.Test;
public class ExampleTest {
@Test
public void test() {
for (int j = 0; j < 4; j++) {
Instant start = Instant.now();
for (int i = 0; i < 10000; i++) {
// Try various implementations here...
String s = "first";
if (true) {
s += "second";
}
if (true) {
s += "third";
}
if (true) {
s += "fourth";
}
}
System.out.println("Took: " + Duration.between(start, Instant.now()).toMillis() + " millis");
}
}
}
如果表现不是问题,那么为人类进行优化并使其尽可能易读和可维护。