【问题标题】:Hashcode deceives me sometimes哈希码有时会欺骗我
【发布时间】:2014-09-24 17:36:30
【问题描述】:

我需要澄清我的方法是对还是错,需要进行任何修改。
让我解释清楚。我将有一个 Excel 文件,其中将有国家代码国家名称年份(mm/yyyy)作为额外的 10 列

countrycode country Name    12/2000 11/2000 10/2000 09/2000 08/2000 07/2000 06/2000 05/2000 04/2000 03/2000 02/2000 01/2000
     IND    India           10.1    10.2    10.3    10.4    10.5    10.6    10.7    10.8    10.9    11.1    11.2    11.3
     USA    Uinted States   8.1     8.2     8.3      8.4    8.5     8.6     8.7     8.8      8.9     9.1    9.2      9.3

如果该特定年份和国家/地区的任何价格重复出现,我需要在 Excel 文件中显示重复的消息。

对于上述,我是通过这种方式实现的。对于 VO 我用哈希码(国家代码 + 年份 + 价格)和 equals 方法覆盖 hashCode() 和 在插入数据库时​​,我将此 VO 传递给 HashSet 并消除重复并将原始列表大小的大小与 HashSet 大小进行比较。

但有时如果有独特的价格,我也会收到重复的消息。

请建议我的方法是对还是错或我可以实施的其他方式。

【问题讨论】:

  • 价格是否存储为浮点数/双精度数?如果是这样,可能是两个看似不同的值用 float/double 表示相同。
  • @nablex 现在只在字符串中
  • 您的方法并非完全错误,尽管您也依赖不相等对象的哈希码来区分,但不一定如此。请提供一个可以重现此问题的最小代码示例。
  • @developer 请考虑重新表述您的问题,并发布一些代码。就目前而言,我真的不明白你做了什么,问题到底是什么。
  • @posdef 确定我会在一小时内更新问题,忙于工作。

标签: java collections


【解决方案1】:

伙计,您采取了正确的想法和方法来解决问题,但只是缺少一点优势(信息)来正确解决问题。

我想提供一点提示,我相信它可以帮助和纠正问题并非常了解基础知识。

如果您查看hashCode 的文档(或源代码)以了解StringDouble 变量,它会声明

字符串

返回此字符串的哈希码。 String 对象的哈希码计算为 s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1] 使用 int 算术,其中 s[i] 是字符串的第 i 个字符,n 是字符串的长度,^ 表示求幂。 (空字符串的哈希值为零。)
返回:此对象的哈希码值。

双重

返回此 Double 对象的哈希码。结果是长整数位表示的两半的异或,完全由方法doubleToLongBits(double) 生成,该 Double 对象表示的原始双精度值。即哈希码是表达式的值:
(int)(v^(v>>>32))
其中 v 由以下内容定义:
long v = Double.doubleToLongBits(this.doubleValue());
返回:此对象的哈希码值。

所以hashCode() 函数在大多数情况下返回一个唯一值,但在很多情况下它为两个对象返回相同的int 值。

我认为你也陷入了同样的境地。

更多提示,您可以使用HashMap<Integer,List<String>>,其中Integer 的值为hashCode,而List<String> 是通过形成String 来自国家代码 + 年份 + 价格。

最后一部分是比较,你可以在计算出的hashCode()的新值处得到List<String>,并检查List中是否存在相同的String值。

【讨论】:

    【解决方案2】:

    基于哈希的集合依赖于hashcode()equals() 方法来正确识别重复项。如果您修改这些以恰好适合一个用例,您可能会在其他用例中产生各种副作用。

    更明确地说。如果您将 VO 的方法更改为仅使用数据的一个子集,您可能会遇到无法预料的问题,因为您可能会在其他地方将 VO 存储在基于哈希的集合中。

    您应该保持 hashcode()equals() 与数据相等性一致,即使用所有属性进行测试,正如许多来源中所建议的那样(eclipse 中的源生成器,来自 Lombok 的 @EqualsAndHashcode 注释,Joshua Bloch 的“Effective Java”,等等)。

    在您的明确情况下,您可以创建一个特定的包装器来根据子集计算您的哈希码和相等性。

    举个例子:

    public void doit(List<VO> vos) {
        Set<VOWrapper> dups = new HashSet<>();
        for (VO vo : vos) {
            if (dups.contains(new VOWrapper(vo))) {
                System.out.println("Found a duplicate");
            } else {
                dups.add(new VOWrapper(vo));
                // Process vo
            }
        }
    }
    

    根据这个VO

    @Data // Lombok generates getters/setters/equals/hashcode (using all fields)
    public class VO {
        private String countrycode;
        private String country;
        private int month;
        private int year;
        private double price;
    }
    

    还有这个包装器

    public class VOWrapper {
    private final VO vo;
    
    public VOWrapper(VO vo) { this.vo = vo; }
    
    // Equals method with only 3 fields used
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        VO other = ((VOWrapper) obj).vo;
        if (vo.getCountry() == null) {
            if (other.getCountry() != null)
                return false;
        } else if (!vo.getCountry().equals(other.getCountry()))
            return false;
        if (vo.getCountrycode() == null) {
            if (other.getCountrycode() != null)
                return false;
        } else if (!vo.getCountrycode().equals(other.getCountrycode()))
            return false;
        if (Double.doubleToLongBits(vo.getPrice()) != Double.doubleToLongBits(other.getPrice()))
            return false;
        return true;
    }
    
    //Hashcode method with only 3 fields used
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((vo.getCountry() == null) ? 0 : vo.getCountry().hashCode());
        result = prime * result + ((vo.getCountrycode() == null) ? 0 : vo.getCountrycode().hashCode());
        long temp;
        temp = Double.doubleToLongBits(vo.getPrice());
        result = prime * result + (int) (temp ^ (temp >>> 32));
        return result;
    }
    }
    

    【讨论】:

      【解决方案3】:

      编写如下代码是完全有效的:

      List<CountryInstance> list = ...;
      Set<CountryInstance> set = new HashSet<CountryInstance>(list);
      if(set.size() < list.size()){
          /* There are duplicates */
      

      要使其工作,您需要值类实例。要创建一个,您需要覆盖等于和哈希码。在你这样做之前阅读What issues should be considered when overriding equals and hashCode in Java?

      【讨论】:

        【解决方案4】:

        如果您只是将所有值解析为字符串,那么您的方法对我来说听起来很合乎逻辑。

        我看了你的描述。您似乎说检测到意外的重复项。所以这真的意味着“等于”方法的行为不像你想象的那样。如果“hashCode”不正确,我认为您会遇到相反的问题(未检测到重复)。

        如果您仍然遇到问题,请附加 'hashCode' 和 'equals' 的实现,它可能有助于快速解决问题。

        还有一件事。我假设所有样本国家在文件中都是唯一的?我的意思是文件后面没有重复的国家/地区?

        【讨论】:

          猜你喜欢
          • 2013-06-29
          • 1970-01-01
          • 1970-01-01
          • 2011-06-27
          • 1970-01-01
          • 1970-01-01
          • 2016-05-20
          • 2023-03-09
          • 2019-06-05
          相关资源
          最近更新 更多