String 拼接别再用 + 号了!90% 的人不知道,性能差 10 倍还易出错

AI 概述
日常开发中 String 拼接常见,“+”号拼接虽顺手却暗藏陷阱。循环里用“+”号拼接性能差百倍,拼接 null 会多出“null”字符串引发业务 bug。文章先剖析底层原理,指出 String 不可变,“+”号拼接在循环中会频繁创建对象。接着通过实测对比不同拼接方式性能,差距明显。还指出“+”号拼接的两个致命坑。最后针对不同场景给出实用拼接建议,如简单拼接用“+”号,循环/大量拼接用 StringBuilder,固定分隔符用 String.join 等,强调避免过度优化与踩坑。
目录
文章目录隐藏
  1. 先拆底层:为啥 + 号拼接这么坑?
  2. 实测见真章:不同拼接方式性能差多少?
  3. + 号拼接的 2 个致命坑(除了性能)
  4. 不同场景的正确写法(实用为主,不搞过度优化)
  5. 最后说句实在的:别过度优化,也别踩坑

在日常开发中,String 拼接操作极为常见,看似简单的“+”号拼接,实则暗藏诸多陷阱。同事代码里常见的日志拼接和循环拼接,虽用“+”号顺手,却可能引发性能灾难和业务 bug。在循环里用“+”号拼接,性能可能差出百倍,数据量越大越明显;拼接 null 时,还会凭空多出“null”字符串,影响业务逻辑。本文将深入剖析“+”号拼接的底层原理,通过实测对比不同拼接方式性能差异,并针对不同场景给出实用拼接建议,助你避开拼接陷阱。

下面是常见的 String 拼接:

// 日常拼接日志
String log = "用户 ID:" + userId + ",操作:" + action + ",结果:" + result;
// 循环拼接数据
String data = "";
for (String s : dataList) {
    data += s;
}

大家都觉得 + 号简单顺手,却没人细想 —— 循环里用 + 号拼接,性能能差出 10 倍不止;甚至拼接 null 时,还会凭空多出 “null” 字符串,踩出莫名其妙的业务 bug。

今天把这事儿掰透:String + 号拼接的坑在哪?不同场景该用啥写法?不用记花里胡哨的技巧,只讲实战中能用的干货。

先拆底层:为啥 + 号拼接这么坑?

想搞懂坑在哪,得先记牢:String 是不可变对象。每一次用 + 号拼接,本质都是创建新的 String 对象 —— 比如 "a" + "b",JVM 会先 new 一个 StringBuilder,调用 append() 拼接,再调用 toString () 生成新 String。

单看这一步,好像没啥问题,但循环里这个优化会彻底失效

// 循环里用 + 号
String result = "";
for (int i = 0; i < 1000; i++) {
    result += "test";
}

这段代码反编译后,相当于每次循环都 new 一个 StringBuilder,拼完就丢,下一次循环再重新 new—— 频繁创建对象 + GC,性能直接拉胯。而如果手动用 StringBuilder,只需要创建一次对象,循环里只调append(),效率天差地别。

实测见真章:不同拼接方式性能差多少?

咱直接跑个测试,拼接 10000 次 “test” 字符串,看看各写法的耗时(单位:毫秒):

import org.junit.Test;

publicclass StringConcatTest {
    privatestaticfinalint TIMES = 10000;

    // 1. + 号拼接(循环)
    @Test
    public void testPlus() {
        String result = "";
        long start = System.currentTimeMillis();
        for (int i = 0; i < TIMES; i++) {
            result += "test";
        }
        long end = System.currentTimeMillis();
        System.out.println("+号拼接耗时:" + (end - start) + "ms");
    }

    // 2. StringBuilder 拼接
    @Test
    public void testStringBuilder() {
        StringBuilder sb = new StringBuilder();
        long start = System.currentTimeMillis();
        for (int i = 0; i < TIMES; i++) {
            sb.append("test");
        }
        String result = sb.toString();
        long end = System.currentTimeMillis();
        System.out.println("StringBuilder 耗时:" + (end - start) + "ms");
    }

    // 3. 指定容量的 StringBuilder(更优)
    @Test
    public void testStringBuilderWithCapacity() {
        // 预估容量:4(test 长度)*10000 = 40000,避免扩容
        StringBuilder sb = new StringBuilder(40000);
        long start = System.currentTimeMillis();
        for (int i = 0; i < TIMES; i++) {
            sb.append("test");
        }
        String result = sb.toString();
        long end = System.currentTimeMillis();
        System.out.println("指定容量 StringBuilder 耗时:" + (end - start) + "ms");
    }
}

实测结果(普通开发机):

  • + 号拼接耗时:128ms
  • StringBuilder 耗时:1ms
  • 指定容量 StringBuilder 耗时:0ms

差距肉眼可见 —— 循环里用 + 号,性能差了上百倍,数据量越大,坑越明显。

+ 号拼接的 2 个致命坑(除了性能)

坑 1:拼接 null 会生成 “null” 字符串

这是最容易踩的业务 bug,比如:

String name = null;
String info = "用户昵称:" + name;
System.out.println(info); // 输出:用户昵称:null

如果是展示给用户的文案、存储到数据库的字段,凭空多出 “null” 绝对是事故 —— 而用 StringBuilder.append (null) 也会这样,必须提前判空:

String info = "用户昵称:" + (name == null ? "" : name);

坑 2:误以为 “编译期拼接” 能覆盖所有场景

有人说 “JVM 会优化 + 号,编译期就拼好”,但这只限于编译期能确定值的场景:

// 编译期拼接,没问题
String s1 = "a" + "b" + "c"; 
// 等同于 String s1 = "abc";

// 运行期拼接,会 new 对象
String s2 = "a";
String s3 = s2 + "b";

别指望 JVM 能把所有 + 号都优化好,循环里的拼接永远是坑。

不同场景的正确写法(实用为主,不搞过度优化)

不用死记规则,按场景选就行:

场景 1:简单拼接(非循环,3 个以内变量)

直接用 + 号,没问题!比如日志拼接、简单文案:

String log = "用户 ID:" + userId + ",操作:" + action;

JVM 会优化成单个 StringBuilder,代码简洁,没必要硬改。

场景 2:循环拼接 / 大量拼接(核心场景)

优先用 StringBuilder(单线程),记得指定初始容量:

// 推荐写法:预估容量,避免扩容
StringBuilder sb = new StringBuilder(1024); // 按业务预估
for (Order order : orderList) {
    sb.append(order.getId()).append(",").append(order.getAmount());
    sb.append("\n"); // 换行
}
String orderStr = sb.toString();

多线程场景用 StringBuffer(线程安全,但性能略低于 StringBuilder)。

场景 3:固定分隔符拼接(比如逗号分隔列表)

用 String.join(),一步到位:

// 拼接列表,逗号分隔
List<String> ids = Arrays.asList("1001", "1002", "1003");
String idStr = String.join(",", ids); // 结果:1001,1002,1003

// 拼接多个字符串,分隔符统一
String info = String.join("|", userId, userName, phone); // 结果:1001|张三|138xxxx8888

场景 4:复杂拼接(前缀 + 后缀 + 多分隔符)

用 StringJoiner,比 StringBuilder 更简洁:

// 需求:拼接成 [1001,1002,1003]
StringJoiner sj = new StringJoiner(",", "[", "]");
sj.add("1001").add("1002").add("1003");
String result = sj.toString(); // 结果:[1001,1002,1003]

最后说句实在的:别过度优化,也别踩坑

String 拼接的核心原则就两点:

  • 简单场景(非循环):+ 号随便用,代码简洁第一;
  • 循环 / 大量拼接:必用 StringBuilder,记得判空 + 指定初始容量;
  • 固定分隔符:优先 String.join (),少写循环少出错。

很多人踩坑不是因为不懂,而是随手写 + 号成了习惯,等到数据量大了才发现性能问题,或者拼接出 “null” 字符串才排查半天。

以上关于String 拼接别再用 + 号了!90% 的人不知道,性能差 10 倍还易出错的文章就介绍到这了,更多相关内容请搜索码云笔记以前的文章或继续浏览下面的相关文章,希望大家以后多多支持码云笔记。

「点点赞赏,手留余香」

0

给作者打赏,鼓励TA抓紧创作!

微信微信 支付宝支付宝

还没有人赞赏,快来当第一个赞赏的人吧!

声明:本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权/违法违规/事实不符,请将相关资料发送至 admin@mybj123.com 进行投诉反馈,一经查实,立即处理!
重要:如软件存在付费、会员、充值等,均属软件开发者或所属公司行为,与本站无关,网友需自行判断
码云笔记 » String 拼接别再用 + 号了!90% 的人不知道,性能差 10 倍还易出错

发表回复