当前位置: 首页 > news >正文

Java String vs StringBuilder vs StringBuffer:一个性能优化的探险故事

原文来自于:https://zha-ge.cn/java/29

Java String vs StringBuilder vs StringBuffer:一个性能优化的探险故事

初遇字符串拼接的噩梦

那是一个阳光明媚的周二下午,我正在为公司的日志系统写一个数据处理模块。需求很简单:把几千条用户操作记录拼接成一个大字符串,然后写入文件。

我满怀信心地写下了这样的代码:

String result = "";
for (LogRecord record : logRecords) {result += record.getTimestamp() + " | " + record.getUserId() + " | " + record.getAction();// ... 其他字段拼接
}

测试时发现,当数据量达到 1 万条时,程序居然跑了 30 多秒!我的第一反应是:“这不科学啊,就是个字符串拼接而已。”

揭开 String 的秘密面纱

经过一番调研,我才恍然大悟。原来 Java 中的 String 就像是一个"顽固的老头"——不可变(Immutable)。每次看似简单的 += 操作,实际上都会:

  1. 创建一个新的 String 对象
  2. 把原来的内容复制过去
  3. 加上新的内容
  4. 抛弃旧对象

想象一下,1 万次循环就是 1 万次"搬家",难怪这么慢!

StringBuilder 的华丽登场

同事小李看到我愁眉苦脸的样子,神秘一笑:“兄弟,试试 StringBuilder 吧。”

StringBuilder sb = new StringBuilder();
for (LogRecord record : logRecords) {sb.append(record.getTimestamp()).append(" | ").append(record.getUserId()).append(" | ").append(record.getAction());
}
String result = sb.toString();

这次测试结果让我惊呆了——同样的 1 万条数据,只用了不到 1 秒!

StringBuilder 就像一个"可扩展的购物袋",内部维护一个字符数组。当空间不够时,它会自动扩容,而不是每次都换个新袋子。

踩坑瞬间:线程安全的陷阱

正当我为找到神器而沾沾自喜时,生产环境出现了奇怪的问题:偶尔会出现字符串内容错乱。经过排查发现,多个线程在并发操作同一个 StringBuilder 实例!

原来 StringBuilder线程不安全的。在多线程环境下,它就像是几个人同时往一个袋子里塞东西,结果可想而知。

这时候,StringBuffer 闪亮登场了:

StringBuffer buffer = new StringBuffer();
// 在多线程环境下安全使用
buffer.append("线程安全的字符串拼接");

三兄弟的特性对比

经过这次"血泪教训",我总结了三者的核心特点:

特性StringStringBuilderStringBuffer
可变性不可变可变可变
线程安全安全不安全安全
性能最慢最快中等
内存开销

使用场景的智慧选择

String:静态字符串的王者

  • 字符串内容不会改变
  • 少量字符串操作
  • 作为方法参数传递

StringBuilder:单线程性能之王

  • 大量字符串拼接操作
  • 单线程环境
  • 对性能要求较高的场景

StringBuffer:多线程的守护者

  • 多线程环境下的字符串操作
  • 需要保证线程安全
  • 可以接受略微的性能损失

经验启示

这次探险让我明白了几个道理:

  1. 没有银弹:每种工具都有其适用场景,关键是理解其特性
  2. 性能测试很重要:看似简单的操作可能隐藏着巨大的性能陷阱
  3. 多线程环境要格外小心:线程安全不是可有可无的"装饰品"

现在,当新同事问我字符串拼接用什么时,我总是会问:“单线程还是多线程?性能要求高吗?”

毕竟,选择合适的工具,就像给脚穿合适的鞋——只有合脚,才能走得更远。


小贴士:在现代 Java 版本中,编译器会自动将简单的字符串拼接优化为 StringBuilder,但复杂场景下,手动选择仍然是最佳实践。


文章转载自:

http://k9WTVcda.nkjnr.cn
http://j3K1jBZS.nkjnr.cn
http://nR4LYcEr.nkjnr.cn
http://nmlvdU2i.nkjnr.cn
http://ISMvIc8H.nkjnr.cn
http://Dq9BCi6T.nkjnr.cn
http://R3avNXNK.nkjnr.cn
http://zBDs2wlk.nkjnr.cn
http://LDMkacsq.nkjnr.cn
http://R2ed2jlf.nkjnr.cn
http://2CyuLeRX.nkjnr.cn
http://WVf8JJAm.nkjnr.cn
http://fXplHwzx.nkjnr.cn
http://2A8p6V0t.nkjnr.cn
http://rNiBYWRk.nkjnr.cn
http://kFsBXAFK.nkjnr.cn
http://wMxH9Ezg.nkjnr.cn
http://4X3E8Siq.nkjnr.cn
http://FdPxrK4g.nkjnr.cn
http://MEAGch5d.nkjnr.cn
http://S9Idi6xt.nkjnr.cn
http://LxOrWArh.nkjnr.cn
http://b7r1stJQ.nkjnr.cn
http://StawIC8S.nkjnr.cn
http://a5VC27ib.nkjnr.cn
http://SFSb1KtY.nkjnr.cn
http://rboxwbLU.nkjnr.cn
http://wfdr4GTQ.nkjnr.cn
http://fXY67An1.nkjnr.cn
http://e4TS4wJl.nkjnr.cn
http://www.dtcms.com/a/366977.html

相关文章:

  • 邦芒干货:新入职场的人必须要知道的三大事情
  • JY-H818|科智立RFID高频读写器产品参数解析
  • LVDS系列27:Xilinx 7系 OSERDESE2原语(三)
  • [晕事]今天做了件晕事91,glibc,rand之前必须设置种子
  • C语言内存精讲系列(七):深入解析 x86 实模式
  • 远场代码学习_FDTD_farfield
  • 五、插值与拟合
  • 今天我们继续学习Linux中的shell脚本流程控制内容
  • 大模型微调之LORA核心逻辑
  • React笔记_组件之间进行数据传递
  • 《Java餐厅的待客之道:BIO, NIO, AIO三种服务模式的进化》
  • 【OpenHarmony文件管理子系统】文件访问接口解析
  • sealos部署k8s
  • (C题|NIPT 的时点选择与胎儿的异常判定)2025年高教杯全国大学生数学建模国赛解题思路|完整代码论文集合
  • 25高教社杯数模国赛【C题国一学长思路+问题分析】第二弹
  • 数学建模25c
  • 互联网大厂Java面试场景与问题解答
  • LeetCode 刷题【64. 最小路径和】
  • Rust+slint实现一个登录demo
  • Rust 文件操作终极实战指南:从基础读写到进阶锁控,一文搞定所有 IO 场景
  • 代码随想录算法训练营第二十八天 | 买卖股票的最佳实际、跳跃游戏、K次取反后最大化的数组和
  • 2025全国大学生数学建模C题保姆级思路模型(持续更新):NIPT 的时点选择与胎儿的异常判定
  • 2025反爬虫之战札记:从robots.txt到多层防御的攻防进化史
  • 23种设计模式——工厂方法模式(Factory Method Pattern)详解
  • C++ 学习与 CLion 使用:(七)if 逻辑判断和 switch 语句
  • docker中的mysql变更宿主机映射端口
  • Redis(43)Redis哨兵(Sentinel)是什么?
  • 【连载 7/9】大模型应用:大模型应用:(七)大模型使用工具(29页)【附全文阅读】
  • 从 GPT 到 LLaMA:解密 LLM 的核心架构——Decoder-Only 模型
  • 原型链和原型