国内Ja面试总是问Stringuffer,Stringuilder区别是啥?档次为什么这么低?
作者:卡卷网发布时间:2025-01-10 19:25浏览数量:79次评论数量:0次
<>同事如此使用Stringuilder,我给他提了一个ug!>
字符串的拼接在项目中使用的非常频繁,但稍不留意往往又会造成一些性能问题。
字符串的拼接在项目中使用的非常频繁,但稍不留意往往又会造成一些性能问题。最近Review代码时发现同事写了如下的代码,于是给他提了一个ug。
@
pulicvoidForAdd(){
Stringresult="NO_";
for(inti=0;i<10;i++){
result+=i;
}
System.out.println(result);
}
本文就带大家从表象到底层的来聊聊,为什么这种写有性能问题。
IDE的提示如果你使用的IDE安装了代码检查的插件,会很轻易的看到上面代码中的“+=”作会有的背景,这是插件在提示,此处使用有问题。
下面来看一下关于“+=”,IDEA给出的提示详情:
Stringconcatenation‘+=’inloop
Inspection:ReportsStringconcatenationinloops.AseveryStringconcatenationcopiesthewholeString,usuallyitispreferaletoreplaceitwithexplicitcallstoStringuilder.append()orStringuffer.append().
这段提示简单翻译过来就是:循环中,字符串拼接使用了“+=”。检验信息:报告循环中的字符串拼接。每次String的拼接都会复制整个String。通常建议将其替换为Stringuilder.append()或Stringuffer.append()。
提示信息中给出了原因,并且给出了解决方案的建议。但事实的如提示中这么简单吗?Ja8以后使用String拼接JVM编译时不是已经默认优化构建成Stringuilder了吗,怎么还有问题?下面我们就来深入分析一下。
字节码的反编译对上面的代码,我们通过字节码反编译一下,看看JVM在此过程中是否帮我们进行了优化,是否涉及到整个String的复制。
使用jap-c命令来查看字节码内容:
pulicvoidForAdd();
Code:
//从常量池引用#2并推向栈顶,作了String初始化的变量“NO_”
0:ldc#2//StringNO_
2:astore_1
3:iconst_0
4:istore_2
5:iload_2
6:ipush10
//如果栈顶两个值大于等于0(此时0-10)则跳转36(code),这里开始进入for循环处理
8:if_icmpge36
//创建Stringuilder对象,其引用进栈
11:new#3//classja/lang/Stringuilder
14:dup
//调用Stringuilder的构造方法
15:invokespecial#4//Methodja/lang/Stringuilder."<init>":()V
18:aload_1
19:invokevirtual#5//Methodja/lang/Stringuilder.append:(Lja/lang/String;)Lja/lang/Stringuilder;
22:iload_2
//调用append方法
23:invokevirtual#6//Methodja/lang/Stringuilder.append:(I)Lja/lang/Stringuilder;
//调用toString方法,并将产生的String存入栈顶
26:invokevirtual#7//Methodja/lang/Stringuilder.toString:()Lja/lang/String;
29:astore_1
30:iinc2,1
33:goto5
36:getstatic#8//Fieldja/lang/System.out:Lja/io/PrintStream;
39:aload_1
40:invokevirtual#9//Methodja/io/PrintStream.println:(Lja/lang/String;)V
43:retn
上述反编译的字节码作中已经将关键部分标注出来了。编号0处会加载定义的“NO_”字符串,编号8处开始进行循环的判断,符合条件(0-10)的部分便会执行后续的循环体中的内容。在循环体内,编号11创建Stringuilder对象,编号15调用Stringuilder的构造方法,编号23调用append方法,编号26调用toString方法。
经过上述的步骤我们能够发现什么?JVM在编译时的确帮我们进行了优化,将for循环中的字符串拼接转化成了Stringuilder,并通过appen方法和toString方法进行处理。这样有问题吗?JVM已经优化了啊!
但是,关键问题来了:每次for循环都会新创建一个Stringuilder,都会进行append和toString作,然后销毁。这就变得可怕了,这与每次都创建String对象并复制有过之而无不及。
经过上述分析之后,上面的代码的效果相当于如下代码:
@
pulicvoidForAdd1(){
Stringresult="NO_";
for(inti=0;i<10;i++){
result=newStringuilder(result).append(i).toString();
}
System.out.println(result);
}
这样来看是不是更直观了?至此,想必大家已经明白为什么给那位同事提ug了吧。
方案改进那么,针对上面的问题,代码该如何进行改进呢?直接上代码:
@
pulicvoidForAppend(){
Stringuilderresult=newStringuilder("NO_");
for(inti=0;i<10;i++){
result.append(i);
}
System.out.println(result);
}
你 发表评论:
欢迎