重要提示:
StringBuffer
本身没有内置的去重方法。去重操作需要结合其他数据结构(如Set
)或算法手动实现。
一、核心思路
由于 StringBuffer
是可变字符串容器,去重的本质是:
- 遍历原字符串中的每个字符。
- 使用一个集合(如
LinkedHashSet
)记录已出现的字符,自动过滤重复。 - 将不重复的字符重新构建为
StringBuffer
。
二、推荐方法:使用 LinkedHashSet
(保持顺序)
✅ 方法 1:转换为 Set 去重(推荐)
import java.util.LinkedHashSet;
import java.util.Set;
public class StringBufferDeduplication {
public static StringBuffer removeDuplicates(StringBuffer sb) {
// 使用 LinkedHashSet 保持字符插入顺序
Set<Character> seen = new LinkedHashSet<>();
// 遍历原 StringBuffer 的每个字符
for (int i = 0; i < sb.length(); i++) {
seen.add(sb.charAt(i));
}
// 构建新的 StringBuffer
StringBuffer result = new StringBuffer();
for (Character ch : seen) {
result.append(ch);
}
return result;
}
public static void main(String[] args) {
StringBuffer sb = new StringBuffer("aabbccdef");
System.out.println("原字符串: " + sb.toString());
StringBuffer unique = removeDuplicates(sb);
System.out.println("去重后: " + unique.toString());
// 输出:abcdef
}
}
✅ 方法 2:原地修改(更节省内存)
public static void removeDuplicatesInPlace(StringBuffer sb) {
Set<Character> seen = new LinkedHashSet<>();
// 从后往前遍历,避免删除后索引错乱
for (int i = sb.length() - 1; i >= 0; i--) {
char ch = sb.charAt(i);
if (!seen.add(ch)) {
// 如果 add 返回 false,说明已存在,删除该位置
sb.deleteCharAt(i);
}
}
}
// 使用示例
StringBuffer sb = new StringBuffer("hello");
removeDuplicatesInPlace(sb);
System.out.println(sb.toString()); // 输出:helo
三、其他方法(按需选择)
方法 3:使用布尔数组(仅适用于 ASCII 字符)
public static StringBuffer removeDuplicatesASCII(StringBuffer sb) {
boolean[] seen = new boolean[256]; // ASCII 字符集
StringBuffer result = new StringBuffer();
for (int i = 0; i < sb.length(); i++) {
char ch = sb.charAt(i);
if (!seen[ch]) {
seen[ch] = true;
result.append(ch);
}
}
return result;
}
优点:时间复杂度 O(n),空间效率高(ASCII 场景)。
缺点:不支持 Unicode 扩展字符。
方法 4:使用 StringBuilder + toCharArray(性能优化)
public static String removeDuplicatesFast(String input) {
if (input == null || input.length() <= 1) return input;
Set<Character> seen = new LinkedHashSet<>();
for (char c : input.toCharArray()) {
seen.add(c);
}
StringBuilder sb = new StringBuilder();
for (Character c : seen) {
sb.append(c);
}
return sb.toString();
}
// 如果必须返回 StringBuffer
StringBuffer result = new StringBuffer(removeDuplicatesFast(sb.toString()));
四、常见错误
❌ 错误 1:直接在正向遍历时删除
// ❌ 错误!索引会错乱
for (int i = 0; i < sb.length(); i++) {
if (isDuplicate(sb, i)) {
sb.deleteCharAt(i); // 删除后,后续字符前移,i 应减1
}
}
❌ 错误 2:使用 HashSet(丢失顺序)
Set<Character> seen = new HashSet<>(); // 无序
问题:去重后字符顺序不确定。
❌ 错误 3:忽略 null 或空值
// 应先判空
if (sb == null || sb.length() == 0) return sb;
五、注意事项
- ✅ 保持顺序:使用
LinkedHashSet
而非HashSet
。 - ✅ 线程安全:
StringBuffer
本身线程安全,但去重逻辑中使用的Set
需注意(单线程用LinkedHashSet
即可)。 - ⚠️ 性能权衡:
LinkedHashSet
:O(n) 时间,保持顺序。- 布尔数组:O(n) 时间,仅限 ASCII。
- ✅ 内存使用:原地修改节省内存,但逻辑稍复杂。
六、最佳实践
场景 | 推荐方法 |
---|---|
一般用途,保持顺序 | LinkedHashSet + 新建 StringBuffer |
内存敏感,允许修改原对象 | LinkedHashSet + 逆序删除 |
仅处理 ASCII 字符 | 布尔数组法(最快) |
单线程高性能 | StringBuilder 替代 StringBuffer |
七、总结
方法 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
LinkedHashSet |
保持顺序,代码清晰 | 额外空间 | 通用推荐 |
原地删除 | 节省内存 | 逻辑复杂 | 内存受限 |
布尔数组 | 性能极高 | 仅 ASCII | ASCII 文本处理 |
💡 一句话总结:
StringBuffer
本身不支持去重,需借助Set
(推荐LinkedHashSet
)遍历过滤重复字符。保持顺序是关键,避免在正向遍历时删除。单线程场景可优先使用StringBuilder
提升性能。