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

Java中StringBuilder原理以及使用

一、StringBuilder特点

StringBuilder是一个可变的字符序列,是String在需要频繁修改字符串时的高效替代品。它的底层基于一个可变的字符数组,其核心优势在于避免了String类不可变性带来的大量临时对象和拷贝开销。它不是线程安全的,但因此获得了更好的性能。


二、底层原理

  1. 继承体系

    java.lang.Object↳ java.lang.AbstractStringBuilder↳ java.lang.StringBuilder

    真正的核心实现(如字符数组和扩容逻辑)都在AbstractStringBuilder中,StringBuilderStringBuffer都继承自它。

  2. 核心结构

    • 一个char[] value;数组,用于存储字符。这就是字符串内容的实际容器。

    • 一个int count;计数器,记录数组中已被使用的字符数量(即当前字符串的length()),而不是数组的总容量。

    // 摘自 AbstractStringBuilder 类(简化版)
    abstract class AbstractStringBuilder {// 存储字符的数组,StringBuilder 可变性的根源char[] value;// 已使用的字符数 (count <= value.length)int count;// ... 其他方法
    }

三、扩容机制

详细步骤说明:

  1. 检查容量: 每当执行append()insert()等可能增加长度的操作时,都会先检查当前字符数组的剩余空间是否够用。
    if (newCount > value.length) expandCapacity(newCount); // 不够就扩容

  2. 计算新容量: 新容量的计算遵循一个“加倍 + 2” 的启发式算法:

    int newCapacity = (value.length * 2) + 2; // 核心算法:旧容量*2 + 2
    if (newCapacity < minCapacity) { // 如果 (旧容量*2+2) 还不够newCapacity = minCapacity;   // 就直接使用所需的最小容量
    }
    if (newCapacity < 0) { // 处理溢出throw new OutOfMemoryError();
    }
    • **为什么是 *2 + 2?** 这是一种空间换时间的权衡。加倍扩容意味着摊销时间复杂度接近O(1)。虽然单次扩容成本是O(n),但多次操作的平均成本很低。+2是为了防止初始容量为0时计算错误。

  3. 数组拷贝: 使用Arrays.copyOf(value, newCapacity)System.arraycopy创建一个新的、更大的数组,并将旧数组的数据拷贝过去。这是扩容过程中最耗时的一步。

e.g.:
假设初始StringBuilder容量为16(默认)。

  • 追加一个长度为10的字符串:容量足够,不扩容。

  • 追加第17个字符:所需最小容量为17。

    • 计算新容量:(16 * 2) + 2 = 34

    • 因为34 > 17,所以新数组容量为34。

  • 如果一次性追加一个长度为100的字符串,(16*2)+2=34仍小于16+100=116,则新容量直接为116。


四、使用方法

StringBuilder的API设计遵循流畅接口(Fluent Interface) 风格,大部分方法都返回this,便于链式调用。

  1. 构造方法

    StringBuilder sb1 = new StringBuilder(); // 默认容量16
    StringBuilder sb2 = new StringBuilder(50); // 指定初始容量,避免频繁扩容
    StringBuilder sb3 = new StringBuilder("Hello"); // 内容初始化为"Hello",容量为16 + 字符串长度
  2. 核心方法

    // 追加(最常用)
    sb.append("World");
    sb.append(100).append(3.14).append('A'); // 链式调用// 插入
    sb.insert(5, " "); // 在索引5的位置插入字符串// 删除
    sb.delete(5, 11); // 删除从5到10的字符
    sb.deleteCharAt(0); // 删除第一个字符// 替换
    sb.replace(0, 5, "Hi"); // 将0到4的字符替换为"Hi"// 反转
    sb.reverse(); // 反转字符序列// 其他
    int length = sb.length();   // 获取长度
    int capacity = sb.capacity(); // 获取当前容量
    sb.setLength(0); // 清空内容(逻辑清空,count=0,但底层数组不变,可重用)
    sb.trimToSize(); // 尝试将容量调整到与实际长度相等,释放多余空间
    String result = sb.toString(); // 转换为不可变的String对象

五、StringBuilder vs StringBuffer

特性StringBuilderStringBuffer
线程安全(关键方法有synchronized修饰)
性能(无同步开销)较低(有同步开销)
使用场景绝大多数场景,单线程环境下多线程共享环境下(现在很少见)

结论:除非你明确需要在多线程环境下共享和修改同一个字符串,否则永远优先使用StringBuilder


六、常见问题总结

Q:“请你讲讲StringBuilder的底层原理和扩容机制。”

A:

StringBuilder的底层是一个可变的字符数组char[] value和一个记录已用字符数的计数器int count。它的可变性就来自于直接修改这个数组,而不是像String那样创建新对象。

它的高效性主要体现在扩容机制上。当现有数组容量不足时,它会创建一个新的更大数组。新容量的计算规则是取(旧容量 * 2 + 2)实际所需最小容量中的较大值。这种‘加倍’的启发式算法旨在以空间换时间,将多次追加操作的平均时间复杂度摊销到接近O(1)。最后通过Arrays.copyOf进行数据拷贝,这是扩容的主要开销。

在实际使用中,如果能预估最终字符串的大致长度,最好使用new StringBuilder(int capacity)指定初始容量,这样可以完全避免扩容带来的性能和内存开销。它和StringBuffer功能几乎一样,但因为它没有用synchronized做线程同步,所以在单线程环境下性能更高,是现在首选的字符串拼接工具。”

http://www.dtcms.com/a/354107.html

相关文章:

  • D4145低功耗GFCI接地故障控制芯片介绍
  • 题目—移除元素
  • 作业帮,途虎养车,得物,途游游戏,三七互娱,汤臣倍健,游卡,快手26届秋招内推
  • JUC多线程个人笔记
  • 【DC工具GUI入门】
  • APP测试全流程以及测试点
  • 【开题答辩全过程】以 基于SpringBoot的流浪动物领养系统的设计与实现 为例,包含答辩的问题和答案
  • 从Java到Go:初遇Go语言的震撼体验
  • 力扣 30 天 JavaScript 挑战 第41天 (第十二题)对异步操作,promise,async/await有了更深理解
  • 【Linux实时内核机制】ww_rt_mutex 的contending_lock异常问题
  • android/java中主线程和子线程的详解
  • Nano Banana揭秘:Google Gemini 2.5 Flash Image正式发布 | AI图像编辑新时代
  • 内网应用如何实现外网访问?外地通过公网地址访问内网服务器的设置方法
  • 动态规划:青蛙跳台阶实践
  • H20 性能表现之 Kimi-K2
  • 【git】:gitee项目管理vs2019
  • 装饰器进阶与设计模式
  • Linux入门教程 第十五章 Linux 系统调优工具
  • 【工具篇】github/huggingface 镜像源总结
  • 嵌入式系统学习Day24(线程)
  • Custom SRP - Shadow Masks
  • Axure:如何将SVG转换为形状
  • leetcode 155 官方golang标准答案错误
  • Java Lambda 处理日期时间 根据区间找出区间内集合
  • Linux程序与进程:核心概念与管理全解析
  • Class45循环神经网络RNN
  • “互联网 +”时代下开源 AI 大模型 AI 智能名片 S2B2C 商城小程序:行业变革与未来展望
  • 基于 Ultralytics YOLO11与 TrackZone 的驱动的高效区域目标跟踪方案实践
  • Python Imaging Library (PIL) 全面指南:PIL基础入门-Python图像处理实战
  • 多版本兼容的golang客服系统