这个Java Comparator如何正常运行?

时间:2011-04-04 17:23:05

标签: java

我最近遇到了一个表面看起来不正确的比较器。但是,我一直无法得到一个输入,导致比较器产生错误的结果。

如果o1< = o2,则错误地将值视为相等,如果o1>则错误地将值返回1 O 2。

我尝试在下面尽可能地简化场景。任何人都可以:

  1. 解释为什么会这样。
  2. 生成一个输入数组,使其无法对输出进行排序。
  3. 我已经尝试了很多,我正在扯皮!

    package comparator;
    
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.Comparator;
    import java.util.List;
    
    public class BadComparator implements Comparator<Integer>
    {
        public int compare(Integer o1, Integer o2)
        {
            // Generally Accepted implementation
            //return o1 - o2; 
    
            // Incorrect(?) Implementation
            return (o2 < o1) ? 1 : 0;
        }
    
        public static void main(String[] args)
        {
            List<Integer> intList = Arrays.asList(10, 9, 8, 1, 2, 3, 7, 4, 5, 6);
            Collections.sort(intList, new BadComparator());
            System.out.println(intList);
        }
    }
    

4 个答案:

答案 0 :(得分:7)

它对我不起作用。它产生的输出为:

[10, 9, 8, 1, 2, 3, 7, 4, 5, 6]

(与输入订单相同)。但这并不能保证......我怀疑它恰好选择了已经按正确顺序排列的元素,或者它决定它们“相等”并让它们独自存在。

请注意,o1 - o2 已损坏...请考虑o1 = Integer.MIN_VALUEo2 = 1 ...您可以通过转换为{{1}来解决此问题当然,首先是价值观。

更自然的实现是

long

或:

return o1.compareTo(o2);

答案 1 :(得分:3)

它是偶然的;它的工作原理取决于Collections.sort()的实现细节。由于比较器没有根据其合同执行它应该执行的操作,因此您无法确定它是否会继续在其他JVM实现或甚至其他版本的Oracle JVM上工作。

尝试找出工作原理并没有多大意义。

答案 2 :(得分:3)

Collections.sort实现为mergesort。查看来源,所有比较条件都是>0<=0,偶然地将负面情况视为相同的情况。不同的排序实现可能会失败。

根据jbellis的评论,“它不是”只是运气,“但是 - Collections.sort保证是”稳定的“,这意味着相等的元素必须在排序后的相对相对顺序。我我不确定用这个比较器来实现一个稳定的排序实现是不可能的,但是我不能想到我的头脑。

private static void mergeSort(Object[] src,
              Object[] dest,
              int low, int high, int off,
              Comparator c) {
int length = high - low;

// Insertion sort on smallest arrays
if (length < INSERTIONSORT_THRESHOLD) {
    for (int i=low; i<high; i++)
    for (int j=i; j>low && c.compare(dest[j-1], dest[j])>0; j--)
        swap(dest, j, j-1);
    return;
}

    // Recursively sort halves of dest into src
    int destLow  = low;
    int destHigh = high;
    low  += off;
    high += off;
    int mid = (low + high) >> 1;
    mergeSort(dest, src, low, mid, -off, c);
    mergeSort(dest, src, mid, high, -off, c);

    // If list is already sorted, just copy from src to dest.  This is an
    // optimization that results in faster sorts for nearly ordered lists.
    if (c.compare(src[mid-1], src[mid]) <= 0) {
       System.arraycopy(src, low, dest, destLow, length);
       return;
    }

    // Merge sorted halves (now in src) into dest
    for(int i = destLow, p = low, q = mid; i < destHigh; i++) {
        if (q >= high || p < mid && c.compare(src[p], src[q]) <= 0)
            dest[i] = src[p++];
        else
            dest[i] = src[q++];
    }
}

答案 3 :(得分:1)

这显然是一个不正确的Comparator实施。例如,Javadoc说:

  

实施者必须确保这一点   sgn(比较(x,y))== -sgn(比较(y,   x))所有x和y。

在这种情况下显然不正确,因为它可以返回1而不是-1。

我相信它在排序时不会引起问题的原因是,使用标准比较器,如果compare方法返回-1或0,则列表中的两个元素处于可接受的顺序,但如果使用标准比较器,则必须交换它们它返回1.因此,至少对于某些排序算法,正确指示第二个参数是否大于第一个参数必须足够。