Java 字符串与+;符号
今天我在读,有一段: 昨天曾经被认为是邪恶的东西(“不要将字符串与+!!!”连接起来”)已经变得酷而高效了!今天JVM将+符号编译成一个字符串生成器(在大多数情况下)。因此,不要犹豫,使用它吧 现在我很困惑,因为他说今天JVM将+符号编译成一个字符串生成器(在大多数情况下),但我以前从未听说过或见过这样的(代码) 有人能举例说明JVM在哪里做这件事,以及在什么情况下做这件事吗 “不要将字符串连接到+!!!” 是错误的,因为它是不完整的,因此具有误导性 规则是 不要在循环中将字符串与+连接起来 这条规则仍然有效。原来的规则从来就不应该应用于循环之外 简单的循环Java 字符串与+;符号,java,performance,jvm,tostring,stringbuilder,Java,Performance,Jvm,Tostring,Stringbuilder,今天我在读,有一段: 昨天曾经被认为是邪恶的东西(“不要将字符串与+!!!”连接起来”)已经变得酷而高效了!今天JVM将+符号编译成一个字符串生成器(在大多数情况下)。因此,不要犹豫,使用它吧 现在我很困惑,因为他说今天JVM将+符号编译成一个字符串生成器(在大多数情况下),但我以前从未听说过或见过这样的(代码) 有人能举例说明JVM在哪里做这件事,以及在什么情况下做这件事吗 “不要将字符串连接到+!!!” 是错误的,因为它是不完整的,因此具有误导性 规则是 不要在循环中将字符串与+连接起来 这
String s = "";
for (int i = 0; i < 10000; i++) { s += i; }
System.out.println(s);
String s=”“;
对于(inti=0;i<10000;i++){s+=i;}
系统输出打印项次;
仍然比以前慢很多
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++) { sb.append(i); }
System.out.println(sb.toString());
StringBuilder sb=新建StringBuilder();
对于(inti=0;i<10000;i++){sb.append(i);}
System.out.println(sb.toString());
因为Java编译器必须将第一个循环转换为
String s = "";
for (int i = 0; i < 1000; i++) { s = new StringBuilder(s).append(i).toString(); }
System.out.println(s);
String s=”“;
对于(int i=0;i<1000;i++){s=new StringBuilder(s).append(i).toString();}
系统输出打印项次;
还有索赔
如今,JVM将+符号编译成字符串生成器(在大多数情况下)
至少有误导性,因为这个转换已经用Java1.0完成了(好的,不是用StringBuilder,而是用StringBuffer,因为StringBuilder只是用Java5添加的)
人们还可以说,这种说法 如今,JVM将+符号编译成字符串生成器(在大多数情况下) 这完全是错误的,因为编译不是由JVM完成的,而是由Java编译器完成的
对于以下问题:Java编译器何时使用
StringBuilder.append()
,何时使用其他机制
Java编译器(1.8版)的源代码包含两个地方,在这两个地方通过+
操作符处理字符串连接
- 首先是字符串常量折叠()。在这种情况下,编译器可以计算结果字符串并处理结果字符串
- 第二个地方是编译器为赋值操作()创建代码的地方。在这种情况下,编译器总是发出代码来创建
StringBuilder
结论是,对于来自OpenJDK的Java编译器(这意味着Oracle分发的编译器),短语在大多数情况下意味着始终(尽管这可能会随着Java 9而改变,或者可能是Eclipse中包含的另一个Java编译器使用了其他机制).当您使用+运算符连接字符串时,编译器会将连接代码转换为使用
StringBuffer
以获得更好的性能。为了提高性能StringBuffer
是更好的选择
使用+运算符连接两个字符串的最快方法
String str = "Java";
str = str + "Tutorial";
编译器将此代码翻译为:
String s1 = "Java";
StringBuffer sb = new StringBuffer(s1);
sb.append("Tutorial");
s1 = sb.toString();
因此,最好使用StringBuffer
或String.format
进行连接
使用String.format
String s = String.format("%s %s", "Java", "Tutorial");
这种形式的声明完全是错误的,它符合这样一种情况:链接的博客继续写废话,就像你必须用
对象包装引用。toString(…)
来处理null
,例如“att1=”+“Objects.toString(att1)+”\'
,而不仅仅是“att1=”“+att1+'\''
。没有必要这样做,显然,作者从未重新检查这些声明
JVM不负责编译+
操作符,因为该操作符只是一个源代码工件。负责编译的是编译器,例如javac
,虽然对编译后的表单没有任何保证,但以下方面鼓励编译器使用生成器:
实现可以选择在一个步骤中执行转换和连接,以避免创建然后丢弃中间字符串对象。为了提高重复字符串连接的性能,Java编译器可以使用StringBuffer类或类似技术来减少cre的中间字符串对象数通过对表达式的求值来定义
请注意,即使编译器不执行此优化,字节码级别上仍然没有+
运算符,因此编译器必须选择JVM理解的操作,例如使用String.concat
,如果您只是concat,这可能比使用StringBuilder
更快恰好启用两个字符串
即使假设字符串连接的编译策略最差(仍在规范范围内),说永远不要将字符串与+
连接是错误的,因为在定义编译时常量时,使用+
是唯一的选择,当然,编译时常量通常比在运行时使用StringBuilder
更有效
在实践中,应用于非常量字符串的+
运算符在Java 5之前被编译成StringBuffer
用法,在Java 5到Java 8中被编译成StringBuilder
用法。当编译的代码与手动使用StringBuffer
对应的StringBuilder
相同时,就不会有性能差异
十多年前,向Java 5的过渡是第一次,通过+
进行字符串连接明显优于手动StringBuffer
使用,因为只需重新编译连接代码
private enum Strategy {
/**
* Bytecode generator, calling into {@link java.lang.StringBuilder}.
*/
BC_SB,
/**
* Bytecode generator, calling into {@link java.lang.StringBuilder};
* but trying to estimate the required storage.
*/
BC_SB_SIZED,
/**
* Bytecode generator, calling into {@link java.lang.StringBuilder};
* but computing the required storage exactly.
*/
BC_SB_SIZED_EXACT,
/**
* MethodHandle-based generator, that in the end calls into {@link java.lang.StringBuilder}.
* This strategy also tries to estimate the required storage.
*/
MH_SB_SIZED,
/**
* MethodHandle-based generator, that in the end calls into {@link java.lang.StringBuilder}.
* This strategy also estimate the required storage exactly.
*/
MH_SB_SIZED_EXACT,
/**
* MethodHandle-based generator, that constructs its own byte[] array from
* the arguments. It computes the required storage exactly.
*/
MH_INLINE_SIZED_EXACT
}