Skip to content

字符串的存储与不可变性

本节学习字符串的存储特点和不可变性。

很多语言中的字符串不是普通可变数组。看起来像是在修改字符串,实际上可能是在创建一个新的字符串对象。理解这一点,可以帮助你分析字符串拼接和修改操作的成本。


从逻辑上看,字符串是一段连续的字符序列。

下标: 0 1 2 3 4
字符: h e l l o

这种结构让字符串可以支持按位置访问、截取子串和顺序遍历。

但不同语言对字符串的底层实现并不完全相同。有些语言的字符串对象不可变,有些语言提供可变字符数组或字符串构建器,用来高效修改文本。


不可变的意思是:字符串对象一旦创建,它的内容不能被原地修改。

例如,看起来像是把字符串拼接了一段内容,实际上可能创建了一个新字符串:

string text = "hello";
text += " world";

这几段代码最终都能得到 hello world。但在 Java、Python 和 C# 中,字符串本身通常是不可变的,拼接会产生新的字符串对象。C++ 的 std::string 更接近可变字符串,某些操作可以在对象内部调整,但也可能涉及重新分配空间。


如果在循环中反复拼接字符串,可能会产生较高成本。原因是每次拼接都可能需要创建新字符串,并复制已有内容。

下面展示一个简单拼接过程:

string result = "";
for (string word : words) {
result += word;
}

如果 words 很多,并且 result 越来越长,反复复制可能造成明显开销。实际开发中,通常会使用更适合的构建方式。


当需要拼接大量字符串时,可以使用字符串构建器或先收集再合并。

string result;
for (string word : words) {
result.append(word);
}

这些写法的共同目标是减少反复创建中间字符串的成本。不同语言有不同推荐方式,但背后的思想一致:不要在大规模循环中无节制地产生大量中间对象。


不可变字符串并不是坏设计。

它有很多优点,例如更安全、便于共享、减少意外修改,也更适合某些缓存和并发场景。但代价是修改字符串时可能需要创建新对象。

可变结构更适合频繁修改,但也需要更小心地管理状态,避免某个地方修改后影响其他逻辑。

这也是数据结构中常见的取舍:安全性、简洁性、空间成本和修改效率之间需要平衡。


字符串通常可以看作连续字符序列,但不同语言对字符串的可变性支持不同。

Java、Python、C# 中的字符串通常不可变,频繁拼接可能产生较高成本;C++ 的 std::string 更灵活,但也可能因为扩容和复制产生开销。处理大量拼接时,应选择更合适的构建方式。