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

C# StringBuilder源码分析

在 .NET 中,StringBuilder 是一个用于高效构建字符串的重要类。它通过避免频繁创建新字符串对象,从而优化了性能。但其背后的实现机制却并不简单。


一、核心字段与属性解析

StringBuilder 内部使用了字符数组(char[])来存储字符串数据,并通过链表的方式管理多个“块”(Chunk),以提升拼接效率。

主要字段:

internal char[] m_ChunkChars;         // 当前块的字符数组
internal int m_ChunkLength;          // 当前块中已使用的字符数
internal int m_ChunkOffset;          // 当前块在整个字符串中的起始位置
internal int m_MaxCapacity;          // 最大容量,默认为 int.MaxValue
internal const int DefaultCapacity = 16;  // 默认初始容量
internal const int MaxChunkSize = 8000;   // 单个 Chunk 的最大长度

Length 属性:

public int Length
{get => m_ChunkOffset + m_ChunkLength;
}

表示当前整个字符串的总长度。


二、构造函数分析

1. 默认构造函数:

public StringBuilder()
{m_MaxCapacity = int.MaxValue;m_ChunkChars = new char[DefaultCapacity]; // 初始大小为16
}

默认分配一个长度为 16 的字符数组。

2. 带字符串参数的构造函数:

public StringBuilder(string value, int startIndex, int length, int capacity)
{...m_ChunkChars = GC.AllocateUninitializedArray<char>(capacity);...
}

根据传入字符串的长度和指定容量,选择较大的值作为初始容量,避免多次扩容。

3. 复制构造函数(用于链表节点创建):

private StringBuilder(StringBuilder from)
{m_ChunkLength = from.m_ChunkLength;m_ChunkOffset = from.m_ChunkOffset;m_ChunkChars = from.m_ChunkChars;m_ChunkPrevious = from.m_ChunkPrevious;...
}

这个构造函数用于创建新的 Chunk 节点,是链表结构的关键。


三、Append 方法的工作原理

Append(char value, int repeatCount) 为例来看 StringBuilder 如何处理追加操作:

public StringBuilder Append(char value, int repeatCount)
{//省略边界检查代码int index = m_ChunkLength;while (repeatCount > 0){if (index < m_ChunkChars.Length){m_ChunkChars[index++] = value;--repeatCount;}else{m_ChunkLength = index;ExpandByABlock(repeatCount); // 扩容并创建新 ChunkDebug.Assert(m_ChunkLength == 0);index = 0;}}m_ChunkLength = index;return this;
}

核心逻辑:

  • 如果当前字符数组还有空间,则直接插入字符。
  • 如果空间不足,调用 ExpandByABlock() 创建新 Chunk,并将其链接到当前 Chunk 的前面。

四、ExpandByABlock 方法详解

该方法负责创建新的 Chunk 并更新当前 Chunk 的状态:

private void ExpandByABlock(int minBlockCharCount)
{int newBlockLength = Math.Max(minBlockCharCount, Math.Min(Length, MaxChunkSize));char[] chunkChars = GC.AllocateUninitializedArray<char>(newBlockLength);m_ChunkPrevious = new StringBuilder(this); // 创建前驱节点m_ChunkOffset += m_ChunkLength;m_ChunkLength = 0;m_ChunkChars = chunkChars;
}

关键步骤:

  1. 计算新 Chunk 的大小:不超过 MaxChunkSize(默认 8000),也不小于所需字符数。
  2. 分配新内存
  3. 创建前驱节点:将当前 Chunk 封装成一个新的 StringBuilder 实例,并赋值给 m_ChunkPrevious
  4. 更新偏移量和长度:当前 Chunk 清空,准备写入新数据。
  5. 切换字符数组:将新分配的数组设为当前 Chunk 使用。

五、为什么使用逆向链表?

每个 StringBuilder 对象维护一个指向“前一个节点”的引用 (m_ChunkPrevious),而不是常见的“后一个节点”。

这样做的好处:

  • 尾部追加操作更高效:由于用户总是从最后一个 Chunk 添加数据,采用“逆向链表”可以快速定位到最后一个节点,无需遍历整个链表。
  • 时间复杂度为 O(1):每次添加新 Chunk 都是在当前节点的基础上创建前驱节点,无需查找最后一个节点。

相比之下,如果使用正向链表(每个节点保存下一个节点引用),则每次添加都需要遍历到末尾,时间复杂度为 O(n),性能下降明显。


六、链表结构带来的代价

虽然链表提升了追加效率,但也带来了一些缺点:

  • 无法随机访问:不能像数组一样直接通过索引访问某个字符。
  • 读取效率较低:若需要从中间或开头插入数据,需遍历整个链表,效率不如单一数组。

因此,StringBuilder 更适合尾部拼接的场景,而不适合频繁的随机修改


七、总结:StringBuilder 的设计哲学

特性实现方式
字符存储使用字符数组(char[]
动态扩容通过链表结构连接多个 Chunk
高效追加使用逆向链表,保持 O(1) 时间复杂度
性能瓶颈不支持随机访问,不适合频繁插入/修改

使用建议:

  • 优先使用构造函数初始化较大容量:减少扩容次数。
  • 避免频繁中间插入操作:这类操作会导致性能下降。
  • 适用于大量字符串拼接场景:如日志记录、HTML 构建等。


文章转载自:
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://.
http://www.dtcms.com/a/281530.html

相关文章:

  • Java大厂面试实录:从Spring Boot到AI微服务架构的层层递进
  • 华为OD 特异双端队列
  • 魔搭官方教程【快速开始】-swift 微调报错:`if v not in ALL_PARALLEL_STYLES`
  • [数据结构]#3 循环链表/双向链表
  • Spring AI Alibaba 1.0 vs Spring AI 深度对比
  • 信息学奥赛一本通 1552:【例 1】点的距离
  • 记一次POST请求中URL中文参数乱码问题的解决方案
  • React响应式组件范式:从类组件到Hooks
  • Ubuntu 安装
  • 回收站里的文件被删除了怎么还原和恢复
  • 京存大容量存储助力“国漫之光”·玄机动画
  • 注解和反射
  • 3D视频技术全解析:从原理架构到产业应用的深度探索
  • Python文本统计分析工具
  • 集训Demo2
  • 巧用Bitset!优化dp
  • “C21988-谷物烘干机(2D+3D+说明书+运动仿真)8张cad+设计说明书
  • Eplan API SQL
  • 从灾前感知到灾后恢复:人工智能在城市气候风险管理中的全链路赋能
  • ESLint 除了在packages.json还能在哪里配置?
  • 【插件】vue-i18n的安装和使用全解
  • Nvidia服务器备份指南 (数据+环境)
  • 高防CDN与普通CDN的核心区别
  • DevOps落地的终极实践:8大关键路径揭秘!
  • Python 字典 (Dictionary) 详解
  • AI产品经理面试宝典第18天:AI思维矩阵构建与实战应用面试题与答法
  • 2HDMI/1DP转EDP/LVDS,支持4K,144HZ和240HZ.
  • zynq分频的例子
  • python学智能算法(十九)|SVM基础概念-超平面
  • Python语法入门之装饰器的基本用法