【问题标题】:Count occurrence HashMap计数出现的HashMap
【发布时间】:2021-12-02 03:22:49
【问题描述】:

我正在尝试使用 hashmap 来统计每天的疾病数量:

public static main(String[] args){
 Disease cholera=new Disease("cholera");
 Disease dengue=new Disease("dengue");

 List<Diagnosis> diagnoses = Arrays.asList(
            new Diagnosis(cholera, 0), // registered cholera on day 0
            new Diagnosis(cholera, 0),
            new Diagnosis(cholera, 1),
            new Diagnosis(cholera, 1),
            new Diagnosis(cholera, 2),
            new Diagnosis(cholera, 2)
    );
   
   printFreq(diagnosis);

}
  
public static void printFreq(List<Diagnosis> diagnoses) {
    Map<Diagnosis, Integer> hm = new HashMap();
    for (Diagnosis x : diagnoses) {

        if (!hm.containsKey(x)) {
            hm.put(x, 1);
        } else {
            hm.put(x, hm.get(x) + 1);
        }
    }

但如果我调用 printFreq(diagnoses) 我会得到:{{cholera, 0}=1, {cholera, 1}=1, {cholera, 2}=1, {cholera, 0}=1, {dengue, 0}=1,{霍乱,1}=1,{霍乱,2}=1}。我该如何解决这个问题 {{cholera,0}=2,{dengue0}=1,{cholera,1}=2,{cholera,2}=2}}

我不能更改诊断或疾病类,但它们看起来是这样的:

public class Disease {

private final String name;

public Disease(String name) {
    this.name = name;
}

public String getName() {
    return name;
}

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    Disease disease = (Disease) o;

    return name.equals(disease.name);
}

@Override
public int hashCode() {
    return name.hashCode();
}

@Override
public String toString() {
    return name;
}

诊断:

 public class Diagnosis {

private final Disease disease;
private final int day;

public Diagnosis(Disease disease, int day) {
    this.disease = disease;
    this.day = day;
}

public Disease getDisease() {
    return disease;
}

public int getDay() {
    return day;
}

@Override
public String toString() {
    return "{" + disease + ", " + day + "}";
}

【问题讨论】:

  • 听起来你真的应该有一个Map&lt;Disease, Integer&gt;而不是Map&lt;Diagnosis, Integer&gt;。毕竟,您正在尝试计算一天疾病的数量......
  • @JonSkeet 我实际上想计算某种疾病每天登记的次数,而不是总数。抱歉,如果我的描述不清楚。
  • 如果您能包含 预期 结果,这将非常有帮助 - 目前我发现很难理解您想要实现的目标。跨度>
  • @sushant:说清楚的方法是在问题中有一个明确的例子。如果有不止一种疾病,如果该示例也显示了这一点,那将非常有帮助......目前它显示霍乱。我不知道 OP 期望如何表示(在输入数据输出数据中)“在第 0 天,我们诊断出 3 例霍乱和 2 例 covid”。
  • @JonSkeet 我完全同意。我看到预期的输出有 {dengue0}=1。所以基于此,我的猜测是 OP 在代码中有错字,或者错过了将new Diagnosis(dengue, 0) 添加到diagnoses 列表中。

标签: java hashmap frequency


【解决方案1】:

您可以添加一个包装类以使生活更轻松。在这段代码中,我有一个包装类DiagnosisMetric,它包装了Diagnosis 类。 以下是printFreq函数的新实现。

    public static void printFreq(List<Diagnosis> diagnoses) {
        Map<DiagnosisMetric, Long> collect = diagnoses.stream().
             collect(Collectors.groupingBy(DiagnosisMetric::new, counting()));
        System.out.println(collect);
    }

而且,以下是包装类。请注意,我已经按照要求实现了equalshashCode

    public static class DiagnosisMetric {
      private Diagnosis diagnosis;

        public DiagnosisMetric(Diagnosis s) {
            this.diagnosis = s;
        }

        public Diagnosis getDiagnosis() {
            return diagnosis;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            DiagnosisMetric that = (DiagnosisMetric) o;
            return diagnosis.getDisease().getName().
              equals(that.getDiagnosis().getDisease().getName())
                    && diagnosis.getDay() == that.getDiagnosis().getDay();
        }

        @Override
        public int hashCode() {
            return Objects.hash(diagnosis.getDay(), 
                   diagnosis.getDisease().getName());
        }

        @Override
        public String toString() {
            String disease = diagnosis.getDisease().getName();
            int day = diagnosis.getDay();
            return "{" + disease + ", " + day + "}";
        }
    }

【讨论】:

    【解决方案2】:

    不需要使用Diagnosis 键存储中间映射的临时解决方案(如前所述,如果没有正确实现hashCodeequals,它不能用作映射键)是这样的:

    • 使用包含diagnosis.daydisease.name 的原始列表作为Collectors.groupingBy 中的密钥包装器
    • 计算频率(例如使用Collectors.summingInt
    • 使用Supplier&lt;Map&gt;提供按列表内容排序的树状图
    • 打印统计数据
    diagnoses.stream()
        .collect(Collectors.groupingBy(
            d -> Arrays.asList(d.getDay(), d.getDisease().getName()),
            () -> new TreeMap<List<?>, Integer> (Comparator
                .comparingInt((List k) -> (Integer) k.get(0))
                .thenComparing((List k) -> (String) k.get(1))
            ),
            Collectors.summingInt(d -> 1)
        )) // Map<List, Integer> created here
        .forEach((k, v) -> System.out.println(k + " = " + v));
    

    输出:

    [0, cholera] = 2
    [0, dengue] = 1
    [1, cholera] = 2
    [2, cholera] = 2
    

    【讨论】:

      【解决方案3】:

      要解决这个问题,我们需要做的是重写 Diagnosis 类的 hashCodeequals 方法,如下所示:

      @Override
      public boolean equals(Object obj) {
          Diagnosis diagnosis = (Diagnosis) obj;
          return this.day == diagnosis.day && this.disease == diagnosis.disease;
      }
          
      @Override
      public int hashCode() {
          char[] charArr = this.disease.toString().toCharArray();
          int sum = 0;
          for (int i = 0; i < charArr.length; i++) {
              sum += charArr[i];
          }
          return sum;
      }
      

      您还需要覆盖Disease 类的equals 方法,下面是代码:

      @Override
      public boolean equals(Object obj) {
          Disease disease = (Disease) obj;
          return this.disease.equalsIgnoreCase(disease.toString());
      }
      

      【讨论】:

        猜你喜欢
        • 2011-05-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-12-09
        相关资源
        最近更新 更多