覆盖`比较方法违反了其一般合同'例外

时间:2017-11-15 13:02:30

标签: scala comparator

我有一个像这样的比较器:

lazy val seq = mapping.toSeq.sortWith { case ((_, set1), (_, set2)) =>
  // Just propose all the most connected nodes first to the users
  // But also allow less connected nodes to pop out sometimes
  val popOutChance = random.nextDouble <= 0.1D && set2.size > 5
  if (popOutChance) set1.size < set2.size else set1.size > set2.size
}

我打算比较集合大小,以便较小的集合在排序列表中可能看起来更高,有10%的可能性。

但编译器不允许我这样做并在我尝试在运行时使用它时抛出异常:java.lang.IllegalArgumentException: Comparison method violates its general contract!。我该如何覆盖呢?

1 个答案:

答案 0 :(得分:4)

我认为这里的问题是,每次比较两个元素时,结果是随机的,因此违反了任何排序算法中比较器函数所需的传递属性。

例如,假设某个实例a比较小于 b,然后b的比较小于c。这些结果应该意味着a的比较小于c。但是,由于您的比较是随机,因此无法保证结果。事实上,您甚至无法保证a下次比较b时会小于mapping

所以不要这样做。没有排序算法可以处理它。 (这种方法也违反了函数式编程引用透明度原则,并且会使您的程序更难以推理。)

相反,您需要做的是在尝试对它们进行排序之前,使用随机分配的权重来装饰地图的成员,以便可以对它们进行一致排序。但是,由于这是在排序操作开始时发生的,因此排序的结果每次都会有所不同,我认为这就是你要找的。

您的示例中不清楚Map[Any, Set[_]]的类型,但它似乎是:mapping。 (您可以根据需要替换类型 - 这对于此方法并不重要。例如,假设Map[String, Set[SomeClass]]实际上具有类型Any,那么您将String下面的引用替换为Set[_] {1}}和Set[SomeClass]case class。)

首先,我们将创建一个mapping,我们将用它来评分和比较地图元素。然后我们将final case class Decorated(x: (Any, Set[_]), rand: Double = random.nextDouble) extends Ordered[Decorated] { // Calculate a rank for this element. You'll need to change this to suit your precise // requirements. Here, if rand is less than 0.1 (a 10% chance), I'm adding 5 to the size; // otherwise, I'll report the actual size. This allows transitive comparisons, since // rand doesn't change once defined. Values are negated so bigger sets come to the fore // when sorted. private def rank: Int = { if(rand < 0.1) -(x._2.size + 5) else -x._2.size } // Compare this element with another, by their ranks. override def compare(that: Decorated): Int = rank.compare(that.rank) } // Now sort your mapping elements as follows and convert back to tuples. lazy val seq = mapping.map(x => Decorated(x)).toSeq.sorted.map(_.x) 的内容映射到此案例类的元素序列。接下来,我们将这些元素排序。最后,我们从装饰类中提取元组。结果应如下所示:

map

这应该将具有较大集合的元素放在前面,但是集合出现5的概率为10%,因此向上移动列表。每次重新执行最后一行时结果都会不同,因为Ordered[T]将为每个元素创建新的随机值。但是,在排序过程中,排名将是固定的,不会改变。

(请注意,我将等级设置为负值。ldap3.Connection特征按升序对元素进行排序,这样 - 如果我们纯粹按设定大小排序 - 较小的集合将在较大的集合之前出现。否定排名值,排序会在较小的集合之前放置较大的集合。如果您不想要这种行为,请删除否定。)