【问题标题】:Java SortedSet + Comparator, consistency with equals() questionJava SortedSet + Comparator,与equals()问题的一致性
【发布时间】:2009-10-02 16:10:32
【问题描述】:

我想要一个按集合大小排序的集合的 SortedSet(在这种情况下是集合本身,但不一定是一般情况)。这似乎违反了让 Comparator 与 equals() 一致的规定——即,两个集合可能不相等(通过具有不同的元素),但比较相同的值(因为它们具有相同数量的元素)。

理论上,我也可以在比较器中加入对大小相等的集合进行排序的方法,但是使用排序不会利用这一点,并且没有真正有用且直观的方法来比较大小相等的集合(至少,在我的特殊情况下),所以这似乎是一种浪费。

这种不一致的情况看起来是个问题吗?

【问题讨论】:

    标签: java equals comparator sortedset


    【解决方案1】:

    SortedSet 接口扩展了核心Set,因此应符合Set 规范中概述的合同。

    实现这一点的唯一可能方法是让您的元素的 equal() 方法行为与您的 Comparator 一致 - 原因是核心 Set 基于相等操作,而 SortedSet 基于比较操作.

    例如,核心 Set 接口中定义的add() 方法指定如果已经有一个元素,其equal() 方法将使用此新元素作为参数返回true,则您不能将元素添加到集合中。好吧,SortedSet 不使用equal(),它使用compareTo()。因此,如果您的compareTo() 返回false,即使equals() 返回true,您的元素也会被添加,从而违反Set 合同。

    然而,这本身并不是一个实际问题。 SortedSet 的行为始终是一致的,即使 compare()equals() 不一致。

    【讨论】:

    • 在这种情况下我更关心相反的方向 - 我想在 equals() 为 false 时将元素添加到集合中,即使 compareTo 返回 0。是否有任何实现那个?
    • 设置实现?不。如果 equals() 返回 false,请不要从 compareTo() 返回零,您将解决所有问题。显然,您需要返回一致的结果,但这不应该太难(最坏的情况是最坏的情况,您可以比较 Set 的哈希码)。性能可能会受到一点影响(这种情况有多普遍,真的吗?),但 Set 和 SortedSet 的语义是一致的。
    【解决方案2】:

    正如 ChssPly76 在评论中所写,在两个集合具有相同大小但不相等的情况下,您可以使用 hashCode 来决定 compareTo 调用。这很好用,除了在极少数情况下,您有两个大小相同的集合,它们不相等,但具有相同的 hashCode。诚然,这种情况发生的可能性很小,但这是可以想象的。如果您想非常小心,请使用 System.identityHashCode 而不是 hashCode。这应该为您提供每个集合的唯一编号,并且您不应该发生冲突。

    归根结底,这为您提供了让集合中的集合按大小排序的功能,在两个大小匹配的集合的情况下可以任意排序。如果这就是您所需要的,它不会比通常的比较慢多少。如果您需要不同 JVM 实例之间的顺序保持一致,这将不起作用,您必须以其他方式进行。

    伪代码:

    if (a.equals(b)) {
        return 0;
    } else if (a.size() > b.size()) {
        return 1;
    } else if (b.size() > a.size()) {
        return -1;
    } else {
        return System.identityHashCode(a) > System.identityHashCode(b) ? 1 : -1;
    }
    

    【讨论】:

    • 在 JVM 问题上不行,因为相同大小的集合的顺序无关紧要。我希望避免这种解决方案,但看起来没有办法。
    • 我不认为这是正确的。从技术上讲,两个包含不同元素的大小相同的集合具有相同的身份哈希码是可能的,在这种情况下,这种比较是不对称的,因为a.compareTo(b)b.compareTo(a) 都返回-1。我们可以通过使用Integer.compare(System.identityHashCode(a), System.identityHashCode(b)) 来避免这个特定问题,但这仍然给我们留下了(a.compareTo(b) == 0) != a.equals(b) 的情况。
    【解决方案3】:

    这似乎违反了禁令 让 Comparator 保持一致 with equals() - 即两个集合 可能不相等(通过有不同的 元素),但比较相同 值(因为它们具有相同的 元素的数量)。

    无论是明示(在 Javadoc 中)还是暗示,都没有要求 Comparator 与对象的 boolean equals(Object) 实现一致。

    请注意,ComparableComparator不同的接口,用途不同。 Comparable 用于定义类的“自然”顺序。在这种情况下,equalscompateTo 不一致是个坏主意。相比之下,Comparator 用于当您想要使用与类的自然顺序不同的顺序时。

    编辑:这是 SortedSet 的 Javadoc 中的完整段落。

    请注意,由 a 维护的排序 排序集(无论是否显式 比较器提供)必须是 如果已排序,则与 equals 一致 set 是正确实现 Set 界面。 (见可比 接口或比较器接口 一致的精确定义 等于。)之所以如此,是因为 设置接口定义为 等于操作,但排序 set 执行所有元素比较 使用它的 compareTo(或比较) 方法,所以两个元素是 这种方法认为相等的是,从 排序集的观点, 平等的。 有序集合的行为是 定义明确,即使它的顺序是 与equals不一致;这只是 不遵守总合同 设置界面。

    我已经突出显示了最后一句话。关键是这样的 SortedSet 将按照您的预期工作,但某些操作的行为不会完全符合 Set 规范……因为规范根据 equals 方法定义了它们的行为。

    所以事实上,对一致性的明确要求(我的错误),但忽略它的后果并没有你想象的那么糟糕。当然,这取决于你是否应该这样做。我估计应该没问题,只要您彻底注释代码并确保 SortedSet 不会“泄漏”。

    但是,我不清楚仅查看集合“大小”的集合比较器是否会起作用……从语义角度来看。我的意思是,你真的想说所有(比如说)2 个元素的集合都是平等的吗?这意味着您的集合只能包含一个任意给定大小的集合...

    【讨论】:

    • javadoc 声明:“排序集维护的排序(无论是否提供显式比较器)必须与 equals 一致”并且与 equals 一致定义为 (compare(e1,e2) ==0)==(等于(e1,e2))。我不确定如何解决您的开场白与 javadoc 中的开场白之间的差异。你能澄清一下吗?
    【解决方案4】:

    Comparator 没有理由返回与equals() 相同的结果。实际上,引入Comparator API 是因为equals() 还不够:如果要对集合进行排序,则必须知道两个元素是小于还是大于。

    【讨论】:

    【解决方案5】:

    SortedSet 作为标准 API 的一部分打破了 Set 接口中定义的约定,并使用 Comparator 来定义相等而不是 equals 方法,这有点奇怪,但事实就是如此。

    如果您的实际问题是根据所包含集合的大小对集合进行排序,则最好使用 List,您可以使用 Collections.sort(List, Comparator>); 对其进行排序

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-02-23
      • 1970-01-01
      • 2010-09-29
      • 1970-01-01
      • 1970-01-01
      • 2021-06-25
      • 2014-10-13
      相关资源
      最近更新 更多