
在字符串类题目中,操作的核心是围绕 String 、StringBuilder、StringBuffer 三个核心类展开的。由于字符串操作在算法题中高频出现(如处理子串、拼接、反转、匹配等),掌握它们的特性和常用方法是关键。以下从「常用类特性」「核心方法」「遍历方式」「构造与修改」「常见场景」五个方面详细说明:
| 类 | 可变性 | 线程安全 | 适用场景 | 效率(修改操作) |
|---|---|---|---|---|
String | 不可变(Immutable) | 安全(无修改方法) | 字符串常量、无需修改的场景 | 低(修改即创建新对象) |
StringBuilder | 可变(Mutable) | 不安全 | 单线程下频繁修改(拼接/删除) | 高(原地修改) |
StringBuffer | 可变(Mutable) | 安全(方法加锁) | 多线程下频繁修改 | 中(锁开销) |
核心结论:
String;StringBuilder(单线程,算法题几乎都是单线程场景)。| 方法 | 功能描述 | 示例( s = "abcabc") |
|---|---|---|
int length() | 返回字符串长度(字符个数) | s.length() → 6 |
boolean isEmpty() | 判断字符串是否为空(长度为 0) | "" → true,"a" → false |
boolean equals(Object obj) | 比较两个字符串的内容是否完全相同(区分大小写) | "abc".equals("ABC") → false |
boolean equalsIgnoreCase(String s) | 比较内容是否相同(忽略大小写) | "abc".equalsIgnoreCase("ABC") → true |
int compareTo(String s) | 按字典序比较(返回差值,0 表示相等,正数表示当前字符串大) | "a".compareTo("b") → -1,"c".compareTo("a") → 2 |
| 方法 | 功能描述 | 示例( s = "abcabc") |
|---|---|---|
char charAt(int index) | 获取指定索引(0-based)的字符(索引越界会抛异常) | s.charAt(2) → 'c' |
String substring(int begin) | 截取子串:从 begin 索引(含)到末尾 | s.substring(2) → "cabc" |
String substring(int begin,int end) | 截取子串:从 begin(含)到 end(不含),即 [begin,end) | s.substring(1,4) → "bca"(索引 1-3 的字符) |
int indexOf(String str) | 返回子串 str 第一次出现的索引,未找到返回-1 | s.indexOf("ab") → 0 |
int indexOf(String str,int from) | 从 from 索引开始,找子串 str 第一次出现的索引 | s.indexOf("ab",1) → 3 |
int lastIndexOf(String str) | 返回子串 str 最后一次出现的索引,未找到返回-1 | s.lastIndexOf("ab") → 3 |
String 不可变,实际返回新对象;StringBuilder 可变,原地修改)方法(String) | 方法(StringBuilder) | 功能描述 | 示例 |
|---|---|---|---|
String concat(String str) | append(String str) | 拼接字符串(String 返回新对象,StringBuilder 原地修改) | "a".concat("b") → "ab"; sb.append("b") |
String replace(char old,char new) | replace(int start,int end,String str) | 替换字符/子串(String 替换所有匹配字符;StringBuilder 替换指定范围) | "aba".replace('a','x') → "xbx" |
String toLowerCase() | toLowerCase() | 转为小写 | "ABC".toLowerCase() → "abc" |
String toUpperCase() | toUpperCase() | 转为大写 | "abc".toUpperCase() → "ABC" |
String trim() | - | 去除首尾空白字符(空格、换行等,中间不变) | "a b".trim() → "a b" |
| - | insert(int offset,String str) | 在 offset 索引处插入字符串 | sb.insert(1, "xy")(原"abc"→"axybc") |
| - | delete(int start,int end) | 删除 [start,end) 范围内的字符 | sb.delete(1,3)(原"abcde"→"ade") |
| - | reverse() | 反转字符串 | sb.reverse()(原"abc"→"cba") |
| 方法 | 功能描述 | 示例 | |
|---|---|---|---|
char[] toCharArray() | 将字符串转为字符数组(便于遍历单个字符) | "abc".toCharArray() → {'a','b','c'} | |
static String valueOf(类型 obj) | 将其他类型(如 int、char[])转为字符串(静态方法) | String.valueOf(123) → "123" | |
String[] split(String regex) | 按正则表达式拆分字符串为数组(注意:. ` | ` 等需转义) | "a,b,c".split(",") → ["a","b","c"] |
byte[] getBytes() | 将字符串按默认编码转为字节数组(IO 操作常用) | "abc".getBytes() → 字节数组 [97,98,99] |
遍历字符串的核心是访问每个字符,常用以下 3 种方式:
charAt(index) 遍历(适合 String)String s = "abc";
for(int i = 0;i < s.length();i++) {
char c = s.charAt(i); // 通过索引获取字符
System.out.print(c + " "); // 输出:a b c
}
toCharArray(),适合频繁访问字符)String s = "abc";
char[] chars = s.toCharArray(); // 转为字符数组
for(int i = 0;i < chars.length;i++) {
System.out.print(chars[i] + " "); // a b c
}
// 增强 for 循环
for(char c: chars) {
System.out.print(c + " "); // a b c
}
StringBuilder 的遍历(同字符数组,因内部是字符数组实现)StringBuilder sb = new StringBuilder("abc");
for(int i = 0;i < sb.length();i++) {
char c = sb.charAt(i); // 同 String 的 charAt
System.out.print(c + " "); // a b c
}
根据场景选择合适的构造方式,影响效率和内存:
String 的构造(不可变)直接赋值(推荐):利用字符串常量池,避免重复创建相同对象。
String s = "abc";(若常量池已有"abc",直接复用引用)
构造方法:创建新对象(即使内容相同),不推荐除非必要。
String s = new String("abc");(一定创建新对象,存在于堆中)
String s = new String(new char[]{'a','b','c'});(从字符数组构造)
StringBuilder 的构造(可变,适合拼接)初始化空对象,再拼接:
StringBuilder sb = new StringBuilder();
sb.append("a").append("b").append("c"); // 链式调用,高效
String result = sb.toString(); // 转为 String:"abc"
初始指定容量(避免动态扩容,更高效):
StringBuilder sb = new StringBuilder(100);(初始容量 100,适合已知大致长度的场景)
StringBuilder)反例(低效):
String s = "";
for(int i = 0;i < 1000;i++) {
s += i; // 每次都会创建新 String 对象,总耗时 O(n²)
}
正例(高效):
StringBuilder sb = new StringBuilder();
for(int i = 0;i < 1000;i++) {
sb.append(i); // 原地修改,总耗时 O(n)
}
String s = sb.toString();
思路:双指针从两端向中间遍历,比较字符是否相等。
public boolean isPalindrome(String s) {
int left = 0,right = s.length() - 1;
while(left < right) {
if(s.charAt(left) != s.charAt(right)) {
return false;
}
left++;
right--;
}
return true;
}
StringBuilder 的 reverse() 方法(最简单):
new StringBuilder(s).reverse().toString();
手动反转(双指针交换字符数组):
char[] chars = s.toCharArray();
int left = 0,right = chars.length - 1;
while(left < right) {
char temp = chars[left];
chars[left] = chars[right];
chars[right] = temp;
left++;
right--;
}
return new String(chars);
思路:用数组(针对 ASCII 字符)或哈希表(针对 Unicode 字符)计数。
public int countChar(String s,char target) {
int count = 0;
for(char c: s.toCharArray()) {
if(c == target) {
count++;
}
}
return count;
}
s 是否包含 t) indexOf():s.indexOf(t) != -1 表示包含。String 的 == 与 equals 区别:
equals 比较内容(String 重写了 equals 方法)。"abc" == new String("abc") → false(不同对象);"abc".equals(new String("abc")) → true(内容相同)。substring 的索引范围:是 [begin,end),即包含 begin,不包含 end,超出范围会抛 StringIndexOutOfBoundsException。
split 方法的坑:
. 拆分需转义:"a.b.c".split("\\.")(因 . 在正则中表示任意字符);"a,,b".split(",") → ["a", "", "b"]。StringBuilder 不是线程安全的:多线程场景用 StringBuffer,但算法题几乎不用考虑线程安全。
字符串操作的核心是:
String 处理静态字符串,用 StringBuilder 处理动态修改;charAt、substring、indexOf 等基础方法,以及遍历、拼接、反转等高频操作;String 的不可变性带来的效率问题,避免在循环中直接修改 String。掌握这些,就能应对绝大多数字符串类算法题。