【问题标题】:How to implement a compareTo() method when consistent with Equal and hashcode与 Equal 和 hashcode 一致时如何实现 compareTo() 方法
【发布时间】:2017-06-28 21:33:18
【问题描述】:

我有一个类Product,其中三个变量:

class Product implements Comparable<Product>{
   private Type type;                 // Type is an enum
   Set<Attribute> attributes;        // Attribute is a regular class
   ProductName name;                 // ProductName is another enum
} 

我使用 Eclipse 自动生成 equal() 和 hashcode() 方法:

@Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((attributes == null) ? 0 : attributes.hashCode());
        result = prime * result + ((type == null) ? 0 : type.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Product other = (Product) obj;
        if (attributes == null) {
            if (other.attributes != null)
                return false;
        } else if (!attributes.equals(other.attributes))
            return false;
        if (type != other.type)
            return false;
        return true;
    }

现在在我的应用程序中,我需要对一组产品进行排序,因此我需要实现 Comparable 接口和 compareTo 方法:

@Override
    public int compareTo(Product other){
        int diff = type.hashCode() - other.getType().hashCode();
    if (diff > 0) {
        return 1;
    } else if (diff < 0) {
        return -1;
    }

    diff = attributes.hashCode() - other.getAttributes().hashCode();

    if (diff > 0) {
        return 1;
    } else if (diff < 0) {
        return -1;
    }

    return 0;
    }

这种实现有意义吗?如果我只想根据“类型”和“属性”值的字符串值对产品进行排序呢?那么如何实现呢?

编辑: 我想对 Set 进行排序的原因是因为我有 Junit 测试,它对 HashSet 的字符串值进行断言。我的目标是在对集合进行排序时保持相同的输出顺序。否则,即使 Set 的值相同,由于 set 的随机输出,断言也会失败。

编辑2: 通过讨论,很明显,在单元测试中断言 HashSet 的 String 值相等是不好的。对于我的情况,我目前编写了一个 sort() 函数来按自然顺序对 HashSet 字符串值进行排序,因此它可以始终如一地为我的单元测试输出相同的字符串值,现在就足够了。谢谢大家。

【问题讨论】:

  • 你为什么曾经在 compareTo 中使用 hashCode?没有意义。什么需要按 hashCode 排序?类的“自然”顺序如何?
  • 好的。没有意义。如何实现类的自然排序?
  • 所以要回答你的问题,你的实现绝对没有意义。
  • 您希望如何订购?依据什么标准? 这是最重要的,也是您应该在 compareTo 中使用的内容。
  • @user697911:您的班级在逻辑上是否具有自然顺序?你想按什么排序?

标签: java equals hashset hashcode


【解决方案1】:

从这里的所有 cmets 看来,您根本不需要使用 Comparator。因为:

1) 您使用的HashSet 不适用于Comparator。它没有被订购。

2) 你只需要确保两个包含Products 的HashSets 是相等的。这意味着它们的大小相同并且包含相同的Products 集。

由于您已经将hashCodeequals 方法添加到Product,您只需在这些HashSets 上调用equals 方法即可。

HashSet<Product> set1 = ...
HashSet<Product> set2 = ...

assertTrue( set1.equals(set2) );

【讨论】:

  • 确实如此,但是在JUnit测试中,我的方法返回了一组Product,我不方便创建另一个Set进行比较,因为创建Product对象并不简单在 Junit 测试中,因为 Product 包含其他对象。断言返回的 Hashset 相等的最简单方法是使用 String 值,因为我可以在 Eclipse Junit 中直接看到 String 值。这就是为什么我要断言 HashSet 的 String 值。
  • 然后在返回的Sets 上调用equals。您永远不应该依赖toString,除非该对象的人工可视化。 Set 上的 toString 可以在不同机器和平台上运行的 JUnit 之间更改。
  • To 等于什么?我有返回的 Set,这很好,但是然后将它与什么进行比较?我没有另一个 Set 可以与 Equal 进行比较,这就是为什么我使用 Set 的 String 值进行比较,因为 String 值是已知的并且很容易获得,但是在我的 Junit 测试代码中创建另一个 Set 是很难。
  • 再次,正确的方法是比较2组。一个是从您的方法返回的,另一个是您需要通过向其添加产品来手动创建的,或者您可以将您拥有的任何字符串表示解析为一组产品。
  • @tsllakp,这是有道理的。我现在所做的是编写一个 sort() 函数,以自然顺序对 hashset 字符串值进行排序,以便每次都能一致地生成相同的字符串表示。
【解决方案2】:

这个实现似乎并不一致。您无法控制 hash codes 的外观。如果您在第一次尝试时根据compareTo 拥有obj1 &lt; obj2,那么下次您启动JVM 时,它可能会与obj1 &gt; obj2 相反。

您真正知道的唯一一件事是,如果 diff == 0 则认为对象相等。但是,您也可以只使用 equals 方法进行该检查。

现在由您决定何时定义obj1 &lt; obj2obj1 &gt; obj2。只要确保它是一致的。

对了,你知道当前的实现在equals检查中不包括ProductName name吗?不知道这是否是故意的。

问题是,您对这些属性了解多少?也许他们实现了Comparable(例如如果他们是Numbers),那么您可以根据他们的compareTo方法订购。如果您对对象完全一无所知,就很难建立一致的顺序。

如果您只是希望它们被一致地排序但排序本身不发挥任何作用,您可以在创建时给它们ids 并按它们排序。此时,您确实可以使用哈希码,如果它可以在 JVM 调用之间更改并不重要,但只有这样。

【讨论】:

  • 是的,实际订购并不重要。我只想根据任何排序实现一致的 HashSet 输出。
  • LinkedHashSet 总是按照你添加元素的顺序输出,也许这就足够了。 HashSet 本身不维护任何顺序,TreeSet 保存使用 compareTo 排序的内容。只要您不重新启动JVM,您的哈希码实现就会定义一个一致的顺序。在下一次开始时,顺序可能会有所不同,只有相同的元素会保持相同,但其余的会改变。
  • LinkedHashSet 会对性能产生影响,这不是我想要的。确保产品集的正确顺序和相同顺序的正确方法是什么?
  • HashSet 和 LinkedHashSet 的唯一区别是它将保存插入顺序(通过 LinkedList)。通过同时持有一个订单是不可能获得 HashSet 的优势(常量获取访问)的。您确定需要HashSet 吗?也许SortedSet 的实现更符合您的需要,例如TreeSet 包含按compareToComparator 排序的元素。无论如何,作为程序员需要指定排序。您的具体需求是什么?这个问题还没有答案吗?
  • 我只需要 TestCase 中的排序,JUnit 就可以断言 HashSet 字符串值。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-08-23
  • 1970-01-01
  • 1970-01-01
  • 2012-03-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多