我有一个像这样的比较器:
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!
。我该如何覆盖呢?
答案 0 :(得分:4)
我认为这里的问题是,每次比较两个元素时,结果是随机的,因此违反了任何排序算法中比较器函数所需的传递属性。
例如,假设某个实例a
比较小于 b
,然后b
的比较小于c
。这些结果应该意味着a
的比较小于c
。但是,由于您的比较是随机,因此无法保证结果。事实上,您甚至无法保证a
下次比较b
时会小于mapping
。
所以不要这样做。没有排序算法可以处理它。 (这种方法也违反了函数式编程的引用透明度原则,并且会使您的程序更难以推理。)
相反,您需要做的是在尝试对它们进行排序之前,使用随机分配的权重来装饰地图的成员,以便可以对它们进行一致排序。但是,由于这是在排序操作开始时发生的,因此排序的结果每次都会有所不同,我认为这就是你要找的。 p>
您的示例中不清楚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
特征按升序对元素进行排序,这样 - 如果我们纯粹按设定大小排序 - 较小的集合将在较大的集合之前出现。否定排名值,排序会在较小的集合之前放置较大的集合。如果您不想要这种行为,请删除否定。)