【问题标题】:Can I use Collections.sort with a quasiorder?我可以将 Collections.sort 与准订单一起使用吗?
【发布时间】:2015-10-13 21:30:50
【问题描述】:

全准序(也称为全预序)是一种较弱的排序关系,允许两个不同的元素被认为是“相同大小”。例如,所有字符串的集合是按长度准排序的,因为两个不同的字符串可以具有相同的长度。

现在假设我们有一个字符串列表,我们想按长度排序(最短的优先)。如果两个字符串的长度相同,我们不关心哪个先出现。 乍一看,这样写似乎是合理的

Collections.sort(list, (s, t) -> s.length() - t.length());

很遗憾,这是非法的。 Comparator 接口的 Javadoc 明确要求比较必须实现总排序。这是违反的,因为 "a".length() - "b".length() 等于 0,但 "a".equals("b") 为假。

那么,我们应该如何干净地做到这一点?干净地说,我的意思是不引入虚假的比较,例如通过哈希码或自然排序。

【问题讨论】:

  • "Comparator 接口的 Javadoc 明确要求比较必须实现全排序。" 是什么给您这样的印象?这是一个总排序;只是根据.equals 不相等的元素可以根据比较器相等。这不仅合法,而且完全正常。它根本不是“与equals一致”。
  • 我认为您混淆了“总排序”和“严格总排序”
  • 与您的问题无关,但我们应该避免使用s.length() - t.length() 作为比较的结果。确实,在处理正值时我们是安全的(就像在这种情况下),但是对于负值,结果可能会从负值溢出到正值,这可能是一个问题。最好改用Integer.compare(s.length(), t.length())。顺便说一句,我们可以使用更清晰的Comparator.comparingInt(String::length),而不是(s, t) -> Integer.compare(s.length(), t.length())
  • @John Graham:总顺序必须满足反对称公理:如果 a

标签: java sorting comparator preorder


【解决方案1】:

您误解了文档。当他们说

一个比较函数,它对某些对象集合进行总排序。

这是一个定义,而不是要求。对于给定的Comparatorc,如果是c.compare(o1, o2) == 0,那么对于c 定义的顺序而言o1o2 是相等的。

文档继续讨论Comparator“与等于一致”意味着什么,这基本上意味着如上所述,Comparator 中固有的平等意义与允许对象的equals() 方法中固有的一个。该讨论基于某些Comparators 将不具有该特征的可能性,而您提出的那个则没有。使用这样的Comparator 来订购SortedSetSortedMap 会产生违反这些接口约定的行为,但是使用这样的ComparatorCollections.sort() 并没有错。

【讨论】:

  • 谢谢,我开始怀疑了。我认为你是对的。
【解决方案2】:

您使用长度的解决方案确实提供了total ordering

"a".length() - "b".length() == 0 所指的内容,但 "a".equals("b") == false 不是总排序。它与与equals一致有关。在这一点上,documentation for the Comparable interface 说:

当且仅当 c.compare(e1, e2)==0 具有与 e1.equals(e2) 相同的布尔值时,比较器 c 对一组元素 S 施加的排序被称为与 equals 一致对于 S 中的每个 e1 和 e2。

这并不意味着您必须提供与 equals 一致的比较器。

【讨论】:

  • 好的,我没有严格要求提供与equals一致的比较器。但是,如果我不这样做,那么我会受到威胁“在使用能够施加与 equals 不一致的排序的比较器来对排序集(或排序映射)进行排序时,应谨慎行事。如果 c 对 S 施加的排序与 equals 不一致,排序集(或排序映射)将表现“奇怪”。特别是排序集(或排序映射)将违反集合(或映射)的一般合同,它是根据等式定义的。所以,我觉得它很脏。
猜你喜欢
  • 1970-01-01
  • 2023-03-25
  • 2020-08-04
  • 2018-10-23
  • 2011-02-22
  • 2021-03-16
  • 2016-11-20
  • 2019-01-25
  • 2011-06-11
相关资源
最近更新 更多