面试题答案
一键面试1. 存储结构
StringBuilder
内部使用字符数组 char[]
来存储字符串内容。它继承自 AbstractStringBuilder
,在 AbstractStringBuilder
类中有一个 char[] value
数组用于存放字符数据。例如:
public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, CharSequence {
// 实际存储字符串的数组
char[] value;
}
2. 扩容机制
- 初始容量:当创建
StringBuilder
对象时,如果没有指定初始容量,默认容量为16。例如StringBuilder sb = new StringBuilder();
此时内部char[] value
的初始大小为16。如果指定了初始容量,例如StringBuilder sb = new StringBuilder(100);
,则char[] value
的初始大小为100。 - 扩容计算:当需要添加的字符数量超过当前容量时,会进行扩容。新容量为原来容量的2倍加2。例如,当前容量为16,当需要扩容时,新容量为
16 * 2 + 2 = 34
。如果新容量仍小于需要的容量(即添加字符后的总字符数),则直接将需要的容量作为新容量。
3. 实际应用中的性能优化
- 预分配足够容量:在已知字符串大致长度的情况下,创建
StringBuilder
对象时指定合适的初始容量,避免多次扩容带来的性能开销。例如,如果你知道最终拼接后的字符串长度大约为1000,那么StringBuilder sb = new StringBuilder(1000);
可以减少扩容操作。 - 减少不必要操作:尽量避免在循环内部进行不必要的
StringBuilder
方法调用。例如,不要在每次循环中都调用toString()
方法,因为每次调用toString()
都会创建一个新的String
对象,应将toString()
操作放在循环外部。
4. 处理非常大的字符串拼接操作的特殊优化手段
- 分段处理:将大字符串按一定规则分成多个小段,分别使用
StringBuilder
进行拼接,最后再将这些拼接后的小段合并。例如,假设要拼接100万个字符,可以每1万个字符为一段,分别拼接后再合并。 - 使用
StringJoiner
:StringJoiner
是Java 8引入的类,用于方便地构建分隔字符串。对于大字符串拼接且需要分隔符的场景,StringJoiner
可以简化代码并可能提高性能。例如,拼接以逗号分隔的字符串列表:
StringJoiner sj = new StringJoiner(",");
for (String str : largeStringList) {
sj.add(str);
}
String result = sj.toString();
- 使用
CharArrayWriter
和BufferedWriter
:CharArrayWriter
可以像StringBuilder
一样动态增长,BufferedWriter
提供了缓冲机制。将字符写入CharArrayWriter
,最后通过toCharArray()
获取字符数组并构建String
。这种方式在处理非常大的字符串时可能更高效。例如:
CharArrayWriter caw = new CharArrayWriter();
BufferedWriter bw = new BufferedWriter(caw);
for (int i = 0; i < veryLargeNumber; i++) {
bw.write("a"); // 写入字符
}
bw.flush();
String result = new String(caw.toCharArray());