1. 概述

在Java开发中,我们经常需要处理字符串操作,其中一项常见任务就是:从目标字符串中移除所有出现在另一个字符串中的字符。这类问题看似简单,但实现方式直接影响性能。

本文将探讨几种不同的实现方案,并分析它们的性能差异。每种方案都经过实战验证,并附有完整的代码示例。

2. 问题场景

先看一个具体例子,假设有两个字符串:

String STRING = "a b c d e f g h i j";
String OTHER = "bdfhj";

我们的目标是:移除STRING中所有出现在OTHER里的字符,最终得到:

"a  c  e  g  i "

注意观察结果:

  • 原字符串中的空格被保留
  • 被移除的字符位置变成了空位(不是删除字符后压缩字符串)

接下来我们将用三种方案解决这个问题,并对比它们的效率。

3. 方案一:嵌套循环

最直观的思路是:

  1. 将两个字符串转为字符数组
  2. 遍历目标字符串的每个字符
  3. 对每个字符,在另一个字符串中查找是否存在
  4. 只保留不存在的字符

代码实现如下:

String nestedLoopApproach(String theString, String other) {
    StringBuilder sb = new StringBuilder();
    for (char c : theString.toCharArray()) {
        boolean found = false;
        for (char o : other.toCharArray()) {
            if (c == o) {
                found = true;
                break;
            }
        }
        if (!found) {
            sb.append(c);
        }
    }
    return sb.toString();
}

⚠️ 关键点

  • 使用StringBuilder而非字符串拼接(+操作符),避免创建大量临时对象
  • 内层循环找到匹配后立即break,减少不必要的遍历

测试验证:

String result = nestedLoopApproach(STRING, OTHER);
assertEquals("a  c  e  g  i ", result);

✅ 测试通过,但性能分析显示:

  • 时间复杂度:O(n²)
  • 当字符串较长时,性能会急剧下降

4. 方案二:使用indexOf()优化

方案一中内层循环可以用String.indexOf()方法替代,这个方法会:

  • 返回字符在字符串中的位置
  • 如果不存在则返回-1

优化后的代码更简洁:

String loopAndIndexOfApproach(String theString, String other) {
    StringBuilder sb = new StringBuilder();
    for (char c : theString.toCharArray()) {
        if (other.indexOf(c) == -1) {
            sb.append(c);
        }
    }
    return sb.toString();
}

测试验证:

String result = loopAndIndexOfApproach(STRING, OTHER);
assertEquals("a  c  e  g  i ", result);

✅ 测试通过,但性能分析:

  • 代码更简洁易读
  • 时间复杂度仍是O(n²)(因为indexOf()内部也是循环实现)
  • 实际运行效率可能略优于方案一(JVM优化)

5. 方案三:使用HashSet

要突破O(n²)的性能瓶颈,需要引入更高效的数据结构。HashSetcontains()方法时间复杂度为O(1),非常适合这个场景。

实现思路:

  1. 将OTHER字符串的所有字符存入HashSet
  2. 遍历STRING字符串,用HashSet快速判断字符是否需要保留
String hashSetApproach(String theString, String other) {
    StringBuilder sb = new StringBuilder();
    Set<Character> set = new HashSet<>(other.length());
    for (char c : other.toCharArray()) {
        set.add(c);
    }

    for (char i : theString.toCharArray()) {
        if (set.contains(i)) {
            continue;
        }
        sb.append(i);
    }
    return sb.toString();
}

性能分析:

  • 构建HashSet:O(n)
  • 遍历STRING并检查:O(n)
  • 总时间复杂度:O(n)

测试验证:

String result = hashSetApproach(STRING, OTHER);
assertEquals("a  c  e  g  i ", result);

✅ 测试通过,性能对比: | 方案 | 时间复杂度 | 适用场景 | |------|------------|----------| | 嵌套循环 | O(n²) | 短字符串,简单场景 | | indexOf | O(n²) | 代码可读性优先 | | HashSet | O(n) | 生产环境首选 |

6. 总结

我们对比了三种实现方案:

  1. 嵌套循环:简单粗暴但性能差,适合快速原型
  2. indexOf优化:代码更简洁,性能无本质提升
  3. HashSet方案:性能最优,推荐在生产环境使用

关键经验:

  • ❌ 避免在循环中使用字符串拼接
  • ✅ 善用Java集合类优化算法
  • ⚠️ 小数据量时性能差异不明显,大数据量时HashSet优势显著

完整代码示例已上传至GitHub仓库,可直接运行测试验证。


原始标题:Remove Characters From a String That Are in the Other String | Baeldung