当我有一个字符串需要将一个字符连接到它的结尾时,我应该更喜欢s=....+']'
超过%s=....+“]”
是否有任何性能原因?
我知道数组字符串连接和字符串生成器,我并不是在询问一般情况下如何连接字符串的建议。
我也知道有些人会有冲动向我解释过早的优化,而且一般来说我不应该为这些小事情费心,请不要...
我之所以问这个问题,是因为从编码风格的偏好来看,我更倾向于使用后一个,但我觉得第一个应该表现得稍微好一点,因为知道所附加的只是一个字符,所以不需要像复制一个字符串时那样对这个字符进行任何内部循环。
更新
正如@scheintod所写的,这的确是一个理论上的问题,我需要做更多的事情来更好地理解java是如何工作的,而不是实际生活中“让我们再节省一微秒”的场景。也许我应该说得更清楚些。
我喜欢理解事情在“幕后”的工作方式,我发现它有时可以帮助我创建更好的代码。
如果它确实改变了我的代码到这样的程度,那么也许我的Q应该是在这个级别,询问它是否适合JIT做这样的事情,如果使用它是否更好。
也许我该看看编译过的字节码...[我将需要学习如何在java中实现...]
作为一个附带说明和示例,我甚至会考虑查看字节码--看看我的一篇关于优化ActionScript2.0-a字节码透视图的相当古老的博客文章--第一部分,它展示了知道代码编译成什么确实可以帮助您编写更好的代码。
除了分析这一点,我们还有另一种可能获得一些见解。我想把重点放在可能的速度差异上,而不是把它们移除的东西上。
因此,让我们从这个test
类开始:
public class Test {
// Do not optimize this
public static volatile String A = "A String";
public static void main( String [] args ) throws Exception {
String a1 = A + "B";
String a2 = A + 'B';
a1.equals( a2 );
}
}
我用javac test.java(使用javac-v:javac 1.7.0_55)编译了这个文件
Compiled from "Test.java"
public class Test {
public static volatile java.lang.String A;
public Test();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]) throws java.lang.Exception;
Code:
0: new #2 // class java/lang/StringBuilder
3: dup
4: invokespecial #3 // Method java/lang/StringBuilder."<init>":()V
7: getstatic #4 // Field A:Ljava/lang/String;
10: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
13: ldc #6 // String B
15: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
18: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
21: astore_1
22: new #2 // class java/lang/StringBuilder
25: dup
26: invokespecial #3 // Method java/lang/StringBuilder."<init>":()V
29: getstatic #4 // Field A:Ljava/lang/String;
32: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
35: bipush 66
37: invokevirtual #8 // Method java/lang/StringBuilder.append:(C)Ljava/lang/StringBuilder;
40: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
43: astore_2
44: aload_1
45: aload_2
46: invokevirtual #9 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
49: pop
50: return
static {};
Code:
0: ldc #10 // String A String
2: putstatic #4 // Field A:Ljava/lang/String;
5: return
}
对于a+“b”
append
使用ljava/lang/String
(字符串)参数调用,而对于a+“b”
使用c
(字符)参数调用。
因此编译不将字符串转换为char,而是将其保持为*。
现在查看AbstractStringBuilder
,它包含我们使用的方法:
public AbstractStringBuilder append(char c) {
ensureCapacityInternal(count + 1);
value[count++] = c;
return this;
}
而且
public AbstractStringBuilder append(String str) {
if (str == null) str = "null";
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
方法实际调用的。
这里最昂贵的操作当然是ensurecapacity
,但仅在达到限制的情况下(它将旧的StringBuffers char[]的数组复制到一个新的数组)。所以这对双方都是正确的,并没有真正的区别。
编辑:当然,这一切都是在JIT发挥魔力之前。请参阅Stephen C的答案。
编辑2:我一直在看Eclipse的编译器生成的字节码,它几乎是一样的。所以至少这两个编译器在结果上没有什么不同。
编辑2:现在有趣的部分
a+"B": 5096 ms
a+'B': 4569 ms
a+'B': 4384 ms
a+"B": 5502 ms
a+"B": 5395 ms
a+'B': 4833 ms
a+'B': 4601 ms
a+"B": 5090 ms
a+"B": 4766 ms
a+'B': 4362 ms
a+'B': 4249 ms
a+"B": 5142 ms
a+"B": 5022 ms
a+'B': 4643 ms
a+'B': 5222 ms
a+"B": 5322 ms
a+'B': 4608ms
a+"B": 5167ms
编辑4:
考虑一下。这是用于基准测试的循环:
start = System.currentTimeMillis();
for( int i=0; i<RUNS; i++ ){
a1 = a + 'B';
}
end = System.currentTimeMillis();
System.out.println( "a+'B': " + (end-start) + " ms" );
因此,我们实际上不仅仅是对我们关心的一件事进行基准测试,而是尽管java循环性能、对象创建性能和变量赋值性能。所以真正的速度差可能还要大一点。
对性能是否有影响? 不同Java版本的行为是否有差异?
问题内容: 我有以下Ecma-Script-6代码 输出如下: 和 我已经能够在此处将字符串串联起来,那么使用模板文字的情形将是什么? 问题答案: 如果像问题示例中那样仅将模板文字与占位符(例如)一起使用,则结果与串联字符串相同。从主观上讲,它看起来更好并且更易于阅读,尤其是对于多行字符串或包含这两者的字符串,因为您不必再转义那些字符了。 可读性是一个很棒的功能,但是关于模板最有趣的是Tagg
问题内容: 这是以前有关Java中的String初始化的一些问题的后续问题。 在用Java进行了一些小测试之后,我面临以下问题: 为什么我可以执行此语句 当str2一个String对象初始化为,但我不能调用方法toString()上str2?那么Java如何将空字符串对象和字符串文字串联起来? 顺便说一句,我还尝试将一个初始化为null和字符串文字的Integer连接起来,”a_literal_s
问题内容: 我正在从事Java代码优化。我不清楚或符号之间的区别: 第2行和第3行有什么区别? 问题答案: 这种方法使用StringBuilder创建结果字符串 此方法仅调用的静态方法来获取int的String版本 依次调用
简介 我只介绍了表和数,因为它们在Scheme中最为常用。然而,Scheme也有像字符(Character)、字符串(String)、符号(Symbol)、向量(Vector)等的其它数据类型,我将在11到14章节中介绍它们。 字符 在某个字符前添加#\来表明该物是一个字符。例如,#\a表示字符a。字符#\Space、#\Tab、#\Linefeed和#\Return分别代表空格(Space)、制
问题内容: 以下语句, 产生输出。 但是,以下内容 产生。 区别在哪里? 问题答案: 您会因为操作符优先级和字符串转换的结合而看到此行为。 JLS 15.18.1 指出: 如果只有一个操作数表达式的类型为String,则对另一操作数执行字符串转换(第5.1.11节),以在运行时生成字符串。 因此,第一个表达式中的右侧操作数将隐式转换为字符串: 但是对于第二个表达式,必须将复合赋值运算符与一起考虑。