Java使用HashMap统计每日疾病确诊数计数重复问题如何修复
问题根因
HashMap判断两个key是否为同一个的逻辑为:先校验两个对象的hashCode()返回值是否相等,若相等再调用equals()方法判断是否返回true,两者同时满足才会判定为同一key。
当前你使用Diagnosis类作为HashMap的key,但Diagnosis类没有重写hashCode()和equals()方法,采用的是Object类的默认实现:默认hashCode()返回对象内存地址映射值,equals()直接对比对象内存地址。因此即使两个Diagnosis实例的disease和day属性完全一致,只要是不同的实例对象,就会被HashMap判定为不同key,无法正确累加计数。
由于你明确要求不能修改Diagnosis和Disease类,因此需要调整HashMap的key规则来解决该问题。
修复方案
我们可以构造一个自定义组合键作为HashMap的key,组合键包含疾病和日期两个属性,且正确实现hashCode()和equals()逻辑即可。这里推荐使用JDK自带的AbstractMap.SimpleEntry作为组合键(该类已经内置了正确的equals和hashCode实现),同时注意修正原main方法中调用printFreq时的参数拼写错误。
修改后的完整代码
import java.util.AbstractMap; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; public class DiagnosisStats { public static void main(String[] args) { Disease cholera = new Disease("cholera"); Disease dengue = new Disease("dengue"); List<Diagnosis> diagnoses = Arrays.asList( new Diagnosis(cholera, 0), new Diagnosis(cholera, 0), new Diagnosis(cholera, 1), new Diagnosis(cholera, 1), new Diagnosis(cholera, 2), new Diagnosis(cholera, 2), new Diagnosis(dengue, 0) ); // 修正原参数拼写错误 printFreq(diagnoses); } public static void printFreq(List<Diagnosis> diagnoses) { Map<AbstractMap.SimpleEntry<Disease, Integer>, Integer> hm = new HashMap<>(); for (Diagnosis x : diagnoses) { AbstractMap.SimpleEntry<Disease, Integer> key = new AbstractMap.SimpleEntry<>(x.getDisease(), x.getDay()); // 简化计数逻辑,无需判断key是否存在 hm.put(key, hm.getOrDefault(key, 0) + 1); } // 按你需要的格式输出结果 for (Map.Entry<AbstractMap.SimpleEntry<Disease, Integer>, Integer> entry : hm.entrySet()) { System.out.printf("{%s, %d}=%d ", entry.getKey().getKey(), entry.getKey().getValue(), entry.getValue()); } } }
如果你使用的JDK版本不支持getOrDefault方法,也可以替换为原有判断逻辑:
if (!hm.containsKey(key)) { hm.put(key, 1); } else { hm.put(key, hm.get(key) + 1); }
可选替代方案
如果不想依赖AbstractMap.SimpleEntry,也可以在当前业务类中自定义一个静态内部类作为组合键:
private static class DiagnosisKey { private final Disease disease; private final int day; public DiagnosisKey(Disease disease, int day) { this.disease = disease; this.day = day; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; DiagnosisKey that = (DiagnosisKey) o; return day == that.day && disease.equals(that.disease); } @Override public int hashCode() { int result = disease.hashCode(); result = 31 * result + day; return result; } }
使用时将HashMap的key替换为DiagnosisKey即可,逻辑和上述实现完全一致。
内容的提问来源于stack exchange,提问作者StephenHawkingi

