重要提示StringBuffer 本身没有内置的去重方法。去重操作需要结合其他数据结构(如 Set)或算法手动实现。


一、核心思路

由于 StringBuffer 是可变字符串容器,去重的本质是:

  1. 遍历原字符串中的每个字符。
  2. 使用一个集合(如 LinkedHashSet)记录已出现的字符,自动过滤重复
  3. 将不重复的字符重新构建为 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;

五、注意事项

  1. 保持顺序:使用 LinkedHashSet 而非 HashSet
  2. 线程安全StringBuffer 本身线程安全,但去重逻辑中使用的 Set 需注意(单线程用 LinkedHashSet 即可)。
  3. ⚠️ 性能权衡
    • LinkedHashSet:O(n) 时间,保持顺序。
    • 布尔数组:O(n) 时间,仅限 ASCII。
  4. 内存使用:原地修改节省内存,但逻辑稍复杂。

六、最佳实践

场景 推荐方法
一般用途,保持顺序 LinkedHashSet + 新建 StringBuffer
内存敏感,允许修改原对象 LinkedHashSet + 逆序删除
仅处理 ASCII 字符 布尔数组法(最快)
单线程高性能 StringBuilder 替代 StringBuffer

七、总结

方法 优点 缺点 适用场景
LinkedHashSet 保持顺序,代码清晰 额外空间 通用推荐
原地删除 节省内存 逻辑复杂 内存受限
布尔数组 性能极高 仅 ASCII ASCII 文本处理

💡 一句话总结StringBuffer 本身不支持去重,需借助 Set(推荐 LinkedHashSet)遍历过滤重复字符。保持顺序是关键,避免在正向遍历时删除。单线程场景可优先使用 StringBuilder 提升性能。