部分有序的比较器

时间:2013-05-22 14:47:07

标签: java algorithm sorting

如何实现根据部分订单关系对其元素进行排序的java.util.Comparator

例如,给定部分订单关系 a c b c ; a b 的顺序未定义。

由于Comparator需要总排序,因此实现会对部分排序未定义但是一致的元素进行排序。

以下是否有效?

interface Item {
    boolean before(Item other);
}

class ItemPartialOrderComperator implements Comparator<Item> {
    @Override
    public int compare(Item o1, Item o2) {
        if(o1.equals(o2)) {  // Comparator returns 0 if and only if o1 and o2 are equal;
            return 0;
        }
        if(o1.before(o2)) {
            return -1;
        }
        if(o2.before(o1)) {
            return +1;
        }
        return o1.hashCode() - o2.hashCode(); // Arbitrary order on hashcode
    }
}
  • 这个比较器的订购是否具有传递性? (我担心不是)
  • Comparators是否需要传递?
    (在TreeMap中使用时)
  • 如何正确实施?
    (如果上述实施不起作用)
    (Hashcodes可能会发生冲突,为简单起见,示例会忽略冲突;有关哈希码的故障安全排序,请参阅Damien B's answerImpose a total ordering on all instances of *any* class in Java。)

6 个答案:

答案 0 :(得分:8)

它似乎更像是一个答案,而不是一个评论所以我会发布它

documentation说:

  

紧跟比较合同后,商是S上的等价关系,强加的排序是S的总订单。“

所以不,比较器需要总排序。如果您通过部分订购实现此操作,则违反了接口合同。

即使它可能在某些情况下有效,您也应该尝试以违反接口合同的方式解决您的问题。

请参阅this question关于适合部分排序的数据结构。

答案 1 :(得分:8)

问题是,当你拥有无法比较的元素时,你需要回到比比较哈希码更聪明的东西。例如,给定部分顺序{a&lt; b,c&lt; d},哈希码可以满足h(d)&lt; h(b)&lt; h(c)&lt; h(a),表示&lt; b &lt; c&lt; d &lt; a(粗体表示由哈希码破坏的领带),这会导致TreeMap出现问题。

一般情况下,除了事先对键进行拓扑排序之外,您可能无需做任何事情,因此欢迎您关注部分订单的一些细节。

答案 2 :(得分:2)

任何时候我都尝试使用哈希码来做这种事情,我后悔了。如果您的订购是确定性的,那么您会更高兴 - 如果没有别的话可用于调试。以下内容将通过为之前未遇到的任何Item创建一个新索引,并在所有其他方法都失败的情况下使用这些索引进行比较。

请注意,订购仍不保证是可传递的。

class ItemPartialOrderComperator implements Comparator<Item> {
    @Override
    public int compare(Item o1, Item o2) {
        if(o1.equals(o2)) {
            return 0;
        }
        if(o1.before(o2)) {
            return -1;
        }
        if(o2.before(o1)) {
            return +1;
        }
        return getIndex(o1) - getIndex(o2);
    }

    private int getIndex(Item i) {
        Integer result = indexMap.get(i);
        if (result == null) {
            indexMap.put(i, result = indexMap.size());
        }
        return result;
    }

    private Map<Item,Integer> indexMap = new HashMap<Item, Integer>();
}

答案 3 :(得分:1)

在jdk7中,您的对象将抛出运行时异常:

  

区域:API:实用工具       概要:数组和集合的更新排序行为可能会抛出 IllegalArgumentException       说明:java.util.Arrays.sort和({间接)java.util.Collections.sort使用的排序算法已被替换   新的排序实现可能会抛出 IllegalArgumentException   检测违反Comparable合同的Comparable。该   以前的实现默默地忽略了这种情况。       如果需要先前的行为,则可以使用新的系统属性java.util.Arrays.useLegacyMergeSort来恢复之前的行为   合并行为。

      不相容的性质:行为
      RFE: 6804124

答案 4 :(得分:0)

如果a < bb < c暗示a < c,那么您已使用hashCodes进行了总排序。拿a < d, d < c。偏序表示bd不一定是有序的。通过引入hashCodes,您可以提供排序。

示例:is-a-descendant-of(human,human)。

Adam (hash 42) < Moses (hash 17), Adam < Joe (hash 9)

蕴涵

Adam < Joe < Moses

一个反面的例子是相同的关系,但是当时间旅行允许成为你自己的后代时。

答案 5 :(得分:-1)

当一个项目不是&#34;之前&#34;也不是&#34;之后&#34;另一个,而不是返回哈希码的比较,只返回0。结果将是&#34;总排序&#34;和&#34;任意&#34;订购重合项目。