1. 前言
分享下关于字符串拼接,应该在什么场景使用什么样的最适合,性能最好,这也是一个很基础的以及面试或者笔试经常会遇到的问题。Ok,let's go !
2. 场景分析
2.1 场景1
public class Main {
public static void main(String[] args) {
String a = "1" + "2";
StringBuilder sb = new StringBuilder();
sb.append("1");
sb.append("2");
StringBuffer buffer = new StringBuffer();
buffer.append("1");
buffer.append("2");
}
}
为了很清晰的看出它们的区别,最好的方式就是直接去反编译这段代码,下面就是反编译出来的内容
![bc7df7417fce2a89b8842aec35bc4ad4.png](https://i-blog.csdnimg.cn/blog_migrate/7b5461bd8a28c15099d8a7ddec151294.jpeg)
下面让我们分析上反编译出来的内容:
- 首先看标记为0的地,后面有个String 12,其实这里对应着我们的第一行String a = "1" + "2";从这里可以看到java在编译期就已经把它拼接成"12"了。
- 我们看下面,可以看到初始化了一个StringBuilder对象,并且调用append方法,其实StringBuilder是在里面维护了一个char[]类型数组用来存储我们的字符串,具体是如何的呢,建立可以看下源码
- 最后就是初始化了一个StringBuffer对象,调用append方法,其实StringBuffer是在StringBuilder的基础上保证了线程安全,append方法加了synchronized,那这里肯定会影响性能,感兴趣的可以看下源码
通过上面简单的分析,相信都知道了,上面这个场景直接使用String效率是最高的,因为这个是在编译器就已经确定好value了,可以看出StringBuffer是个线程安全,所以相对会牺牲掉一些性能,当然jdk1.6后对于synchronized进行了很大的优化,所以性能也还不错。 所以下面重点分析String和StringBuilder的区别。
1.2 场景2
public class Main {
public static void main(String[] args) {
String a = "1";
String b = "2";
String c = a + b;
StringBuilder sb = new StringBuilder();
sb.append(a);
sb.append(b);
}
}
一样的,我们来看看反编译后的内容:
![a8d693ee375403dfa5415910a0b7a05f.png](https://i-blog.csdnimg.cn/blog_migrate/614670dc24cc6b1a1bfe216af4a2d557.jpeg)
然后再分析下上面反编译的内容:
- 从0标记的位置到3标记的位置,代表着我们定义了a和b的变量,并且值为"1"和"2",然后就到了我们String c = a + b,可以看到在反编译内容中的6标记的位置,是初始化了一个StringBuilder,然后使用了append方法进行拼接了,从这里就可以很清楚直白的看出,
原来java把我们的String + 号优化成StringBuilder的append了 - StringBuilder部分跟之前分析的一样
我相信看到这里,可以很明显的知道了,当我们使用String拼接变量的时候,jvm会帮我们优化成StringBuilder来进行拼接,所以如果是像上面这样的场景使用拼接,两者性能是没有差异的。
1.3 场景3
public class Main {
public static void main(String[] args) {
String a = "";
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10; i ++) {
a += "hello";
sb.append("hello");
}
}
}
我们继续来看反编译后的内容:
![966f5f6b7cd6d4a1e43cf8fad6f6f594.png](https://i-blog.csdnimg.cn/blog_migrate/614bf71a528d5b36052acc034688b4ff.jpeg)
同样的套路,我们来分析下:
- 首先在写的代码中是循环十次,每次都进行累加拼接"hello"这个字符串,我们可以看到在反编译内容中,在 a += "hello"这个位置是初始化了一个StringBuilder对象,可能会疑惑了,为什么我拼接的是常量"hello"啊,为什么会jvm会优化成StringBuilder,不应该是编译就确定好才对吗?其实这里因为a += "hello"其实就是a = a + "hello",这里可以看到a是个变量,所以java无法提前预知,所以就转换成了StringBuilder。
- 然后我们看反编译的19标记的地方到35的地方,在for循环内,每次都会创建一个StringBuilder对象,这里就很明显了,每次创建对象,浪费了很大的空间,并且创建需要开销,所以,可以看出这里使用String拼接,性能就会很低。
3. 尾语
相信通过上面的三个场景分析,可以很明显的看出在什么情况下,使用哪个进行拼接性能最好,最合适,最优雅。最后我们在总结下,在拼接都是常量的时候,使用String性能最高,在有变量的时候使用StringBuilder性能比较好,在多线程安全情况下,使用StringBuffer是最合适的。切记不要在循环里面使用String拼接变量。
4、公众号
欢迎大家加入到我的公众号。公众号: MonarchCode,如果你想学习更多的关于分布式、微服务、k8s、docker 等等干货,那赶紧关注获取一波学习文章,更是有我录播的免费视频观看,只要关注即可哦!
5. QQ 群
欢迎大家加入我创建的 QQ 群,QQ 群号: 963643807 或者扫描二维码入群,会不定期分享资源,分享技术、视频,让大家在技术成长、进阶的路上更加畅通无阻!
![e7296820ac692b2b9a173935d002bed0.png](https://i-blog.csdnimg.cn/blog_migrate/2148a34a5024de442bef20c54b1bca7b.jpeg)
5. 相关说明
转载本文,记得附上本文地址链接!