【问题标题】:Java's Comparator ContractJava 的比较器契约
【发布时间】:2014-04-07 10:27:46
【问题描述】:

Comparator 接口的约定中说,它必须与 equals 一致。

这是否意味着 Comparator = 0 如果 equalsTo = true ,还是意味着 Comparator = 0 如果 且仅当 equalsTo = true ?

我似乎记得这是第二个,但我遇到了很多按非唯一子属性排序的比较器。

例如,我可能有具有子属性日期的对象,我想按提交日期对我的对象列表进行排序。但是,您可以拥有多个具有相同日期的对象吗?这有什么后果?当然,这个问题已经有最佳实践解决方案了吗?如何在不违反比较器合同的情况下按不能保证唯一的属性对集合进行排序?这种违规行为的后果是什么?它们是否易于管理?

【问题讨论】:

    标签: java collections comparator


    【解决方案1】:

    Comparator 必须与 equals 一致是完全不正确的。

    文档只是针对这种情况发出警告:

    在使用能够施加与 equals 不一致的排序的比较器对排序集(或排序映射)进行排序时应谨慎 (http://docs.oracle.com/javase/7/docs/api/java/util/Comparator.html)

    如果您有一个基于日期的排序,另一个基于日期+时间的排序,您应该简单地实现多个比较器。

    也许您将 Comparator 与 Comparable 混淆了?对于 Comparable,文档强烈建议反对这种情况:

    强烈建议(尽管不是必需的)自然排序与 equals 一致。 (http://docs.oracle.com/javase/7/docs/api/java/lang/Comparable.html)

    如果您意识到一个对象只能有 1 个 Comparable 实现,但可以有多个 Comparator,则这种差异是有意义的。 Comparator 的整个想法是有多种方法来比较同一个类。

    【讨论】:

    • 谢谢,这是因为我使用了一个带有比较器的排序集(TreeSet),它有一个相当意外的(对我来说)结果,它删除了比较器==0的元素,而我认为它只会删除equalsTo = true的元素。显然,这不是一件很难解决的事情,但让我感到惊讶。
    【解决方案2】:

    edit 你可以有多个 Comparators,并且正如 popovitsj 所说,它们不一定要与 equals 一致

    (虽然我认为你大部分时间都有Comparator.compare(obj1, obj2) == 0 obj1.equals(obj2) == true

    如果你想在按非唯一字段排序时得到特定的排序结果,你需要自定义你的Comparator来考虑这些,

    例如,在实现compare() 时遇到obj1.date == obj2.date,那么您应该比较其他重要字段(姓名、年龄等)以相应地对obj1obj2 进行排名并返回相应的值。

    希望对您有所帮助。

    【讨论】:

      【解决方案3】:

      正如您所怀疑的,为了使compareTo()equals() 保持一致,compareTo() 必须始终equals() 返回true 时返回0。同样,如果equals 返回false,那么compareTo 不得返回0

      但是,正如 popovitsj 在他的回答中指出的那样,equals() 的一致性不是必需的。因此,仅当您尝试使两种方法保持一致时,上述内容才适用。

      【讨论】:

        【解决方案4】:

        Comparator 接口的约定中说,它必须与 equals 一致。

        这并不完全正确;请参阅@popovitjs 的回答。

        这是否意味着 Comparator = 0 如果 equalsTo = true ,还是意味着 Comparator = 0 当且仅当 equalsTo = true 时?

        意思是后者。但是,对于 Comparator 对象,这实际上并不是硬性要求。

        我似乎记得这是第二个,但我遇到了很多按非唯一子属性排序的比较器。

        这是合理的,因为它实际上并不是一个硬性要求。事实上,与equals(Object) 不一致的Comparator 可以与Arrays.sort(...) 一起使用。只有TreeSetTreeMap 会出现问题。

        例如,假设您有一个Comparator<E> C,它表示e1e2 不相等,但e1.equals(e2) 返回true。现在假设您使用比较器创建了一个TreeSet<E> 实例,然后将e1e2 添加到该集合中。集合的树是基于比较器组织的,因此e1e2 将插入搜索树中的不同位置,并且都是集合的元素。但这违反了Set 的主要不变量...它基于equals 方法。

        正如 TreeSet 的 javadoc 所说:

        "请注意,如果要正确实现 Set 接口,集合维护的顺序(无论是否提供显式比较器)必须与 equals 一致。(请参阅 Comparable 或 Comparator 以获得精确定义与 equals 一致。)之所以如此,是因为 Set 接口是根据 equals 操作定义的,但 TreeSet 实例使用其 compareTo(或 compare)方法执行所有元素比较,因此该方法认为相等的两个元素是,从集合的角度来看,等于。集合的行为是明确定义的,即使它的顺序与等于不一致;它只是不遵守 Set 接口的一般约定。"

        这回答了你问题的最后一部分。

        这种违规行为的后果是什么?

        如果您在 TreeSet 或 TreeMap 中使用不一致的 Comparator,则该集合将不遵守 SetMap 协定。

        【讨论】:

          【解决方案5】:

          ab 可能不相等。但是,如果比较 ab 时比较器为零,则比较 ba 时应该为零。 p>

          this answer 中说:

          通常情况下,如果从 equals 角度看 2 个对象相等,但从 compareTo 角度看不相等,则可以将这两个对象作为键存储在 TreeMap 中。这可能导致不直观的行为。也可以在特定情况下故意这样做。

          但这是针对特定情况,一般来说,没有什么能阻止你在 equals 和 compareTo 行为不一致时出现不一致的行为。

          举个例子,今天早上有人问:Move specific items to the end of a list

          大多数答案都有比较器,对于不相等的元素返回 0。

          【讨论】:

            猜你喜欢
            • 2022-01-04
            • 2018-03-05
            • 1970-01-01
            • 1970-01-01
            • 2011-05-30
            • 1970-01-01
            相关资源
            最近更新 更多