说实话,刚看到 IDEA 提示我把StringBuilder换成+号拼接字符串的时候,我内心是拒绝的。毕竟我们这些“老兵”从小就被教育:字符串拼接要用StringBuilder,+性能差、内存浪费、GC 压力大... 那时候用+拼接可是要被师傅敲脑壳的。
结果现在 IDEA 突然改口让我用+,我真有点怀疑这货是不是喝多了
但冷静一想,IDEA 再怎么说也是个“智能”工具,它不会无缘无故做这种推荐吧?于是我决定撸起袖子,手动跑一下 benchmark,看看到底谁更快,谁是背锅侠。
我先写了两个方法,一个是用传统StringBuilder拼接,另一个是直接用+:
public String concatWithPlus(int count) {
String result = "";
for (int i = 0; i < count; i++) {
result += i;
}
return result;
}
public String concatWithBuilder(int count) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < count; i++) {
sb.append(i);
}
return sb.toString();
}
然后我用System.nanoTime()简单测了一下在拼接 10 万次的情况下,两者的耗时:
concatWithPlus: 33501ms
concatWithBuilder: 36128ms
嗯?这玩意儿还真的是+更快??这就离谱了啊!我赶紧反编译了一下 class 文件,用javap -c看了眼字节码,结果一看才明白,原来在普通的非循环拼接场景下,编译器其实已经偷偷帮我们把+转成了StringBuilder,就像这样:
new StringBuilder().append("abc").append("123").toString();
也就是说,在非循环的场景里,用+和用StringBuilder编译出来压根就是一回事,性能几乎没差,但代码却可以简洁不少。IDEA 的建议,其实是基于这种优化来的——也就是说,它没喝多,是我们还活在 JDK 1.4 的世界里 🤯
不过注意哈,这优化仅限于“编译期可确定的拼接”,像这样:
String s = "Hello" + name + ", welcome!";
只要name不是循环变量,基本都能被优化掉。
那问题来了——是不是以后我们都该扔掉StringBuilder,一律用+呢?当然不是!别急着把 StringBuilder 拉黑,它还是有一席之地的。
比如我把拼接操作放到循环里:
public String loopConcatWithPlus() {
String result = "";
for (int i = 0; i < 10000; i++) {
result += i;
}
return result;
}
再对比一下StringBuilder版本:
public String loopConcatWithBuilder() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++) {
sb.append(i);
}
return sb.toString();
}
这下结果就非常刺激了:
loopConcatWithPlus: 46285ms
loopConcatWithBuilder: 1317ms
注意,是将近35倍的性能差距,用 IDEA 自带的 profiler 都能看到内存分配飙升得像坐火箭一样。为啥差这么多?因为在循环里每次result += i都会创建一个新的StringBuilder实例,再转成字符串,根本没法复用,性能自然扑街
所以,在循环拼接里用+就是性能灾难,IDEA 可不会给你背这个锅。
你以为这就完了?还有一类同学特别爱写这种模式:
String s = "";
for (...) {
s += someFunction(i);
}
然后问题还不出在他们机器上,因为他们只测了 100 次,没感觉出差距,一上线就拉了全公司后腿... 这就是典型的“用法没错,场景不对”。
那总结下来说
如果你只是在方法里简单拼接几个字符串,比如拼个日志、URL、字段之类的,不用多想,直接用+,省事又清爽,编译器会替你兜底。
但如果你在循环里疯狂拼接,请一定用StringBuilder,不然性能就跟 PPT 一样“流畅”。
至于那些为了性能强行用StringBuilder的场景,如果不是循环,其实真没必要。写得复杂,还不如让编译器帮你干脏活。
哦对了,有同学说那是不是可以用StringJoiner,我只能说除非你真的需要定制 separator 或者 prefix/suffix,否则也挺鸡肋。说到底,性能和可读性之间的平衡,不是哪个 API 更先进,而是你是不是在对的场景用对了方法。
其实我们这一代人对+的偏见,说白了就是对编译器的不信任,但 Java 编译器这几年真的越来越聪明了,连 lambda、stream 的优化都能搞定,不该再活在“+拼接都是傻子”那种旧观念里了。
你说 IDEA 为啥老提示我们用+?人家不是为了节省你那点性能,而是为了提升代码可读性、降低维护成本。如果你每次为了拼接两三个字段都祭出StringBuilder,那不是高级程序员,是“仪式感程序员”。
所以最后的建议就是:别用惯性写代码,用脑子写。看到 IDEA 提示,先别怒删,要想一想,它背后的逻辑是不是比我们先入为主的印象更靠谱。
不过话说回来,现在 IDEA 连 switch 写错都能自动 refactor,有时候我还真挺担心:未来我们是不是都会变成“代码搬砖工”,一切全靠提示...
那你呢?你会相信 IDEA 的提示,还是坚持祖传的手艺?
最后,我为大家打造了一份deepseek的入门到精通教程,完全免费:https://www.songshuhezi.com/deepseek