
本文讲解在Java中比较两个列表中对象的字符串属性(如器官名称)时,为何不能使用==而必须用equals(),并通过器官捐献注册系统实例说明修复方法、代码优化与常见陷阱。
本文讲解在Java中比较两个列表中对象的字符串属性(如器官名称)时,为何不能使用`==`而必须用`equals()`,并通过器官捐献注册系统实例说明修复方法、代码优化与常见陷阱。
在Java中,字符串是引用类型,== 比较的是两个变量是否指向内存中的同一对象实例,而非内容是否相等。即使两个 String 对象内容完全相同(例如都为 "heart"),若由不同构造方式生成(如字面量 vs new String()),== 仍可能返回 false。因此,比较字符串内容必须使用 .equals() 方法。
回到你的器官捐献系统,问题核心出现在嵌套循环中的这一行:
if(oNeeded.get(k).getOrgan() == oDonated.get(i).getOrgan()) // ❌ 错误!
应修正为:
if("".equals(oNeeded.get(k).getOrgan()) ?
"".equals(oDonated.get(i).getOrgan()) :
oNeeded.get(k).getOrgan().equals(oDonated.get(i).getOrgan())) // ✅ 安全写法更简洁且推荐的写法(避免空指针):
if(Objects.equals(oNeeded.get(k).getOrgan(), oDonated.get(i).getOrgan()))
✅ 前提:在类顶部添加 import java.util.Objects;
此外,原代码存在多个可优化点:
- 逻辑位置问题:匹配检测被放在“添加捐赠者”分支内,导致仅在新增捐赠时才尝试匹配,而遗漏了“先有需求后有捐赠”的场景。理想做法是提供独立的“匹配并输出”功能(如菜单选项4),或在每次添加后统一触发匹配。
- 重复扫描开销:每次添加捐赠者就遍历全部需求列表,时间复杂度 O(n×m),大数据量下效率低;可考虑用 Map<String, List<OrganInfo>> 按器官索引需求方,实现 O(1) 查找。
- 输入处理缺陷:sc.next() 不读取换行符,后续 nextLine() 可能跳过输入;建议统一用 sc.nextLine() 并注意清理缓冲区。
以下是修复后的关键片段(含健壮性增强):
import java.util.*;
// ... 其他导入
public class OrganDonation {
// 使用泛型明确类型,避免强制转换
private static ArrayIndexedList<OrganInfo> oDonated = new ArrayIndexedList<>();
private static ArrayIndexedList<OrganInfo> oNeeded = new ArrayIndexedList<>();
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
boolean running = true;
System.out.println(menuOfOptions());
String input = sc.nextLine().trim();
while (running) {
switch (input) {
case "1":
System.out.print("For who? "); String need = sc.nextLine().trim();
System.out.print("What organ? "); String organ1 = sc.nextLine().trim();
oNeeded.add(new OrganInfo(organ1, need));
System.out.println("✓ Recipient registered.\n");
break;
case "2":
System.out.print("By who? "); String donor = sc.nextLine().trim();
System.out.print("What organ? "); String organ2 = sc.nextLine().trim();
oDonated.add(new OrganInfo(organ2, donor));
System.out.println("✓ Donor registered.");
performMatching(); // 独立匹配方法,清晰可复用
break;
case "3":
running = false;
System.out.println("Goodbye!");
break;
default:
System.out.println("Invalid selection. Please try again.\n");
}
if (running) {
System.out.println(menuOfOptions());
input = sc.nextLine().trim();
}
}
sc.close();
}
// ✅ 核心修复:使用 Objects.equals 安全比较字符串
private static void performMatching() {
for (int i = 0; i < oDonated.size(); i++) {
String donatedOrgan = oDonated.get(i).getOrgan();
for (int k = 0; k < oNeeded.size(); k++) {
String neededOrgan = oNeeded.get(k).getOrgan();
if (Objects.equals(donatedOrgan, neededOrgan)) {
System.out.printf("%s's %s was matched to %s%n",
oDonated.get(i).getPerson(),
donatedOrgan,
oNeeded.get(k).getPerson()
);
// 可选:移除已匹配项防止重复匹配
// oNeeded.remove(k--); break;
}
}
}
}
// ... OrganInfo 内部类(保持不变,但建议将字段设为 final)
public static class OrganInfo {
private final String _organ;
private final String _person;
public OrganInfo(String organ, String person) {
this._organ = organ == null ? "" : organ.trim();
this._person = person == null ? "" : person.trim();
}
public String getOrgan() { return _organ; }
public String getPerson() { return _person; }
}
public static String menuOfOptions() {
return """
1.) Register organ needed.
2.) Register organ donated.
3.) Exit.
----------------------------
Select 1, 2 or 3: """;
}
}注意事项总结:
- ✅ 永远用 Objects.equals(a, b) 替代 a == b 比较字符串内容(自动处理 null);
- ✅ 将匹配逻辑抽离为独立方法,提升可读性与可测试性;
- ✅ 输入后调用 .trim() 防止前后空格导致匹配失败;
- ⚠️ 若需真实业务系统,应增加器官兼容性校验(如血型、HLA配型),而不仅是名称匹配;
- ? 后续可升级为使用 HashMap<String, Queue<OrganInfo>> 实现 O(1) 匹配,并支持优先级队列管理等待列表。
掌握字符串比较的本质差异,是写出健壮Java程序的第一道关键门槛——它不只关乎器官匹配,更关乎所有基于内容判断的业务逻辑可靠性。