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. 方案一:嵌套循环
最直观的思路是:
- 将两个字符串转为字符数组
- 遍历目标字符串的每个字符
- 对每个字符,在另一个字符串中查找是否存在
- 只保留不存在的字符
代码实现如下:
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²)的性能瓶颈,需要引入更高效的数据结构。HashSet
的contains()
方法时间复杂度为O(1),非常适合这个场景。
实现思路:
- 将OTHER字符串的所有字符存入HashSet
- 遍历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. 总结
我们对比了三种实现方案:
- 嵌套循环:简单粗暴但性能差,适合快速原型
- indexOf优化:代码更简洁,性能无本质提升
- HashSet方案:性能最优,推荐在生产环境使用
关键经验:
- ❌ 避免在循环中使用字符串拼接
- ✅ 善用Java集合类优化算法
- ⚠️ 小数据量时性能差异不明显,大数据量时HashSet优势显著
完整代码示例已上传至GitHub仓库,可直接运行测试验证。