在 Java 中,并没有一个叫做 StringDynamicArray 的原生类,当我们谈论“字符串的动态数组”时,通常指的是以下两种情况:

- 可以动态增长的
String数组:使用 Java 集合框架中的ArrayList<String>。 String本身的内部字符数组:String对象使用一个char数组来存储字符,这个数组在创建时大小固定,但 Java 提供了一些机制(如StringBuilder)来高效地“动态”地构建字符串。
下面我们分别详细讲解这两种情况。
使用 ArrayList<String> 实现动态字符串数组
这是最常见、最推荐的方式,Java 的集合框架提供了 ArrayList 类,它内部使用一个数组来存储元素,但当数组空间不足时,它会自动创建一个更大的新数组,并将旧数组的元素复制过去,从而实现了“动态”增长。
ArrayList<String> 的核心特点:
- 动态大小:可以随时添加或删除元素,无需关心底层数组的大小。
- 类型安全:通过泛型
<String>,确保列表中只能存储String对象,避免了类型转换的麻烦和运行时错误。 - 有序:元素的存储顺序就是它们被添加的顺序。
- 允许重复元素:可以存储多个相同的
String。
常用操作示例:
import java.util.ArrayList;
public class StringArrayListExample {
public static void main(String[] args) {
// 1. 创建一个 String 类型的 ArrayList
ArrayList<String> names = new ArrayList<>();
// 2. 添加元素 (add)
names.add("Alice");
names.add("Bob");
names.add("Charlie");
System.out.println("初始列表: " + names); // 输出: [Alice, Bob, Charlie]
// 3. 在指定位置添加元素
names.add(1, "David"); // 在索引 1 的位置插入 "David"
System.out.println("插入后: " + names); // 输出: [Alice, David, Bob, Charlie]
// 4. 获取元素 (get)
String firstPerson = names.get(0);
System.out.println("第一个名字是: " + firstPerson); // 输出: Alice
// 5. 修改元素 (set)
names.set(2, "Eve"); // 将索引 2 的元素 "Bob" 修改为 "Eve"
System.out.println("修改后: " + names); // 输出: [Alice, David, Eve, Charlie]
// 6. 删除元素 (remove)
names.remove("David"); // 删除值为 "David" 的元素
// names.remove(0); // 也可以按索引删除,例如删除索引为 0 的元素
System.out.println("删除后: " + names); // 输出: [Alice, Eve, Charlie]
// 7. 获取列表大小 (size)
System.out.println("列表大小: " + names.size()); // 输出: 3
// 8. 遍历列表
System.out.println("遍历列表:");
for (String name : names) {
System.out.println(name);
}
// 9. 检查元素是否存在 (contains)
System.out.println("列表中是否包含 'Alice'? " + names.contains("Alice")); // 输出: true
// 10. 转换为固定大小的数组 (toArray)
String[] namesArray = names.toArray(new String[0]);
System.out.println("转换为数组后的第一个元素: " + namesArray[0]); // 输出: Alice
}
}
String 内部的字符数组与 StringBuilder
String 对象在 Java 中是不可变的(immutable),这意味着一旦一个 String 对象被创建,它的内容就不能被改变,任何看起来像是修改字符串的操作(如 concat, replace),实际上都是创建了一个新的 String 对象,而原来的对象保持不变。
如果需要进行大量的字符串拼接或修改操作,频繁地创建新 String 对象会非常低效,这时,我们就需要使用“可变”的字符序列,也就是 StringBuilder 或 StringBuffer。

StringBuilder 的核心特点:
- 可变性:
StringBuilder对象内部的字符数组可以被修改,而不会创建新的对象。 - 高性能:对于字符串的拼接、插入、删除等操作,
StringBuilder的性能远高于直接使用String的 号或concat方法。 - 非线程安全:它的方法不是同步的,因此在单线程环境中使用效率更高,如果需要在多线程环境中使用,应使用
StringBuffer(它是线程安全的,但性能稍差)。
StringBuilder 的工作原理(动态数组思想):
StringBuilder 内部维护一个可变的 char[] 数组,当你调用 append() 方法时:
- 如果当前数组还有剩余空间,就直接将新字符添加到末尾。
- 如果空间不足,它会自动创建一个更大的新数组(通常是容量的 2 倍 + 2),将旧数组的内容复制到新数组,然后添加新字符。
这个过程与 ArrayList 的扩容机制非常相似,都是动态数组思想的体现。
StringBuilder 操作示例:
public class StringBuilderExample {
public static void main(String[] args) {
// 1. 创建一个 StringBuilder 对象
StringBuilder sb = new StringBuilder();
// 2. 添加字符/字符串 (append)
sb.append("Hello");
sb.append(" ");
sb.append("World");
System.out.println("拼接后: " + sb.toString()); // 输出: Hello World
// 3. 插入字符/字符串 (insert)
sb.insert(5, ","); // 在索引 5 的位置插入 ","
System.out.println("插入后: " + sb.toString()); // 输出: Hello, World
// 4. 删除字符 (delete)
sb.delete(5, 7); // 删除从索引 5 (包含) 到 7 (不包含) 的字符,即删除 ", "
System.out.println("删除后: " + sb.toString()); // 输出: HelloWorld
// 5. 反转字符串 (reverse)
sb.reverse();
System.out.println("反转后: " + sb.toString()); // 输出: dlroWolleH
// 6. 获取最终字符串 (toString)
// 当操作完成后,调用 toString() 方法将其转换为一个不可变的 String 对象
finalResult = sb.toString();
System.out.println("最终结果: " + finalResult);
}
}
总结与对比
| 特性 | ArrayList<String> |
String / StringBuilder |
|---|---|---|
| 用途 | 存储多个字符串的集合,像一个动态的字符串列表。 | 存储和操作单个字符串序列。 |
| 核心思想 | 一个可以动态增长的 String 对象的容器。 |
一个内部使用动态数组的、可变的字符序列。 |
| 可变性 | ArrayList 本身是可变的(可以增删改元素),但它存储的 String 对象是不可变的。 |
String 不可变;StringBuilder 可变。 |
| 性能 | 增删元素有开销(特别是中间位置),但比频繁创建 String 高效得多。 |
StringBuilder 的拼接操作非常高效,避免了创建大量临时 String 对象。 |
| 如何选择 | - 当你需要一个列表来存放一组字符串时(学生名单、单词列表)。 - 当你需要对字符串集合进行排序、搜索等操作时。 |
- 当你需要在一个循环中频繁地拼接、修改一个字符串时(构建 SQL 语句、JSON 数据)。 - 当你需要从不同来源高效地组合一个最终字符串时。 |
- 想管理一堆字符串,用
ArrayList<String>。 - 想高效地构建或修改一个字符串,用
StringBuilder。

