【问题标题】:logical inconsistence between Set and SortedSet interfaces in Java [closed]Java中Set和SortedSet接口之间的逻辑不一致[关闭]
【发布时间】:2013-01-26 07:05:11
【问题描述】:

我注意到 Java 中的 Set 和 SortedSet 接口之间存在逻辑上的不一致。

SortedSet 将不同的对象(通过 equal() 方法)识别为相等,如果它们在比较期间相同,但它在逻辑上是不正确的。对象的比较应该只负责对象的顺序。

例如:我可以有很多产品,我想按价格对它们进行排序。在这种情况下,SortedSet 不能包含价格相同的不同产品: [“盐”,0.5 美元], [“牛奶”, 1 美元], [“面包”, 1 美元], [“香蕉”, 2 美元] 在上面的例子中,牛奶将被面包代替。在这种情况下,继承的 Set 接口的约定将被违反,因为不相等的对象相互替换。我红色了 SortedSet 的 JavaDoc,并且知道这种行为有据可查,但我认为这是一个逻辑上的失败。

你的意见是什么,也许你已经对 Set 和 SortedSet 有过类似的问题?

【问题讨论】:

  • Why 所以您认为使用 equals() 进行相等是不正确的? '不相等的对象互相替换'是什么意思?
  • 有时我想按名称排序产品,有时按日期排序。如果我直接在 Product.equals() 方法中实现按价格比较,那么我不能按日期对产品进行排序。此外,从逻辑上讲,如果价格相同,产品就不等于。
  • 那你需要使用两个不同的比较器。

标签: java collections set sortedset


【解决方案1】:

这不是逻辑上的失败,而是设计的属性。请注意,如果您的比较算法与 equals 一致,则a.compareTo(b) == 0 将等效于a.equals(b)

javadoc 实际上非常明确:

一个有序集合的行为是明确定义的,即使它的排序与equals不一致;它只是不遵守 Set 接口的一般约定。

有用吗?

这实际上在您想要达到特定行为的地方提供了灵活性。例如,假设您想将字符串放入 SortedSet 并让它们忽略大小写进行排序,您可以使用:

Set<String> set = new TreeSet<> (String.CASE_INSENSITIVE_ORDER);

这样会达到预期的效果,但是这样:

set.add("ABC");
set.add("abc");

只会将一个字符串添加到您的集合中,因为它们将被视为与该特定比较器相等。

【讨论】:

  • 感谢您的回答,有时我想按名称排序产品,有时按日期排序。如果我直接在产品类中实现比较方法,并将产品定义为按价格相等,那么我将无法按日期对产品进行排序。我认为“排序”和“公平”是不同的方面,应该分开。
  • 这种说法的问题在于,实际上没有一种有效的方法来实现将相等与比较分开的数据结构。
  • 感谢您的理解,我基于 apache collections 中的 TreeList 实现了我的 TreeSet 版本。它工作正常,我用 apache 集合 SetTest 对其进行了测试。我将在我的开源库的下一个版本中发布它。新的 TreeSet 不通过比较器而是通过 equals() 方法将元素识别为相等,并且向下兼容普通 Set。这样,新的 TreeSet 实现将包含与 HashSet 相同的元素,但采用有序样式。
【解决方案2】:

您可能指的是equals() 方法,它实际上在Object 中可用,因此Java 中的每个对象类型要么覆盖它,要么从Object 继承它。

【讨论】:

    【解决方案3】:

    在Java中,一般来说,ab这两个对象如果a.equals(b) == true是一样的。而一个Set,每一个Set,甚至SortedSet,都没有重复,所以如果你重写equals来比较price属性,如果两个对象的价格相同,则它们是相同的。

    我猜你不会覆盖 equals 方法,而是提供一个 ComparatorSortedSet 来进行排序。在这种情况下,正如另一个答案中所说,如果a.compareTo(b) == 0 将等同于a.equals(b)

    如果你想对那个 Set 排序,你需要另一个结构,比如一个 List 来进行排序。比如:

    List<Product> list = new ArrayList<Product>(myUnorderedProductsSet);
    Collections.sort(list, comparatorByPrice);
    

    请注意,这不会加倍或复制您的 Product Bean,而只是另一种结构,以不同的顺序引用相同的对象。所以这不是昂贵或可以避免的事情。

    【讨论】:

    • 如果你想订购那个 Set,你需要的是另一个结构 => 不确定我是否跟随你:根据定义,SortedSet 是...排序的。所以不需要再显式排序了。
    • 两个对象相等,如果 obj1.equals(obj2)==true 而不是对象相互比较。例如,有时我想按名称对产品进行排序,而其他时间按日期排序。如果我直接在产品类中实现比较方法并按价格比较产品,那么将无法按日期对产品进行排序。我认为“排序”和“公平”是不同的方面,应该分开。
    • @assylias 是的,Set 已排序,但也没有重复的元素,因为您选择的排序标准只是作为一个 Set。您需要另一个不保留重复元素的结构。例如,一个列表。然后你可以保留重复的元素。
    • @thinker 我想我知道你的意思,但 Java 不能那样工作。
    • 感谢您的理解,我基于 apache collections 中的 TreeList 实现了我的 TreeSet 版本。它工作正常,我用 apache 集合 SetTest 对其进行了测试。我将使用我的开源库的下一个版本发布它。新的 TreeSet 不通过比较器而是通过 equals() 方法将元素识别为相等,并且向下兼容普通 Set。这样,新的 TreeSet 实现将包含与 HashSet 相同的元素,但采用有序样式。
    猜你喜欢
    • 2016-11-08
    • 1970-01-01
    • 2011-02-11
    • 1970-01-01
    • 2019-05-11
    • 1970-01-01
    • 2013-12-03
    • 2013-07-17
    • 1970-01-01
    相关资源
    最近更新 更多