Java 找出比较器可能无法工作的情况

Java 找出比较器可能无法工作的情况,java,sorting,comparator,Java,Sorting,Comparator,我想对列表(整数列表)进行排序,这样包含数字3的列表就被放在列表的顶部,剩下的元素的现有顺序保持不变 final ArrayList<ArrayList<Integer>> arrayLists = Lists.newArrayList( Lists.newArrayList(1, 2), Lists.newArrayList(1, 2, 3), Lists.newArrayList(1, 2), L

我想对列表(整数列表)进行排序,这样包含数字3的列表就被放在列表的顶部,剩下的元素的现有顺序保持不变

    final ArrayList<ArrayList<Integer>> arrayLists = Lists.newArrayList(
        Lists.newArrayList(1, 2),
        Lists.newArrayList(1, 2, 3),
        Lists.newArrayList(1, 2),
        Lists.newArrayList(1, 3),
        Lists.newArrayList(1, 4),
        Lists.newArrayList(1, 2, 3)
    );
    System.out.println(arrayLists);
初始尝试使用下面的比较器

Comparator<List<Integer>> c = new Comparator<List<Integer>>() {
    @Override
    public int compare(final List<Integer> o1, final List<Integer> o2) {
        System.out.println("Compare " + o1 + "<=>" + o2);
        if (o1.contains(3))
            return -1;
        return 0;
    }
};

Collections.sort(arrayLists, c);
System.out.println(arrayLists);
比较器c=新比较器(){ @凌驾 公共整数比较(最终列表o1,最终列表o2){ System.out.println(“比较”+o1+“”+o2); 如果(o1.包含(3)) 返回-1; 返回0; } }; Collections.sort(arrayLists,c); System.out.println(arrayLists); 返回

Compare [1, 2, 3]<=>[1, 2]
Compare [1, 2]<=>[1, 2, 3]
Compare [1, 2]<=>[1, 2]
Compare [1, 3]<=>[1, 2]
Compare [1, 3]<=>[1, 2, 3]
Compare [1, 4]<=>[1, 2]
Compare [1, 4]<=>[1, 2]
Compare [1, 2, 3]<=>[1, 2]
Compare [1, 2, 3]<=>[1, 2, 3]
Compare [1, 2, 3]<=>[1, 3]

[[1, 2, 3], [1, 3], [1, 2, 3], [1, 2], [1, 2], [1, 4]]
比较[1,2,3][1,2] 比较[1,2][1,2,3] 比较[1,2][1,2] 比较[1,3][1,2] 比较[1,3][1,2,3] 比较[1,4][1,2] 比较[1,4][1,2] 比较[1,2,3][1,2] 比较[1,2,3][1,2,3] 比较[1,2,3][1,3] [[1, 2, 3], [1, 3], [1, 2, 3], [1, 2], [1, 2], [1, 4]] 这是预期的(所有包含3个的列表都位于顶部)

然而,深入研究表明

实现者必须确保所有x和y的sgn(compare(x,y))==-sgn(compare(y,x))

实现者还必须确保关系是可传递的:((compare(x,y)>0)&(compare(y,z)>0))意味着compare(x,z)>0

最后,实现者必须确保compare(x,y)==0意味着所有z的sgn(compare(x,z))==sgn(compare(y,z))

这不是完全实现上述比较器,它可以很容易地断言与测试下面

    final ArrayList<Integer> x = Lists.newArrayList(1, 2, 3);
    final ArrayList<Integer> y = Lists.newArrayList(1, 2);
    System.out.println(c.compare(x,y));
    System.out.println(c.compare(y,x));


Compare [1, 2, 3]<=>[1, 2] => -1
Compare [1, 2]<=>[1, 2, 3] => 0 which is not -(-1)
final ArrayList x=Lists.newArrayList(1,2,3);
最终ArrayList y=Lists.newArrayList(1,2);
系统输出println(c.compare(x,y));
系统输出println(c.compare(y,x));
比较[1,2,3][1,2]=>-1
比较[1,2][1,2,3]=>0,该值不是-1
是否有任何方法可以证明上述比较器不适用于某个特定的示例列表(其中不将包含3的列表放在顶部)

有没有办法证明上述比较器在某些情况下不起作用

对。最明显的原因是它可以返回
-1
,但不能返回正数。这显然违反了第一条规则

不违反规则的比较器是

Comparator<List<Integer>> c = new Comparator<List<Integer>>() {
    @Override
    public int compare(final List<Integer> o1, final List<Integer> o2) {
        return Integer.compare(o1.contains(3) ? 0 : 1, o2.contains(3) ? 0 : 1);
    }
};
比较器c=新比较器(){ @凌驾 公共整数比较(最终列表o1,最终列表o2){ 返回整数比较(o1.contains(3)?0:1,o2.contains(3)?0:1); } }; 在Java8中,您可以将其简化为

Comparator<List<Integer>> c = Comparator.comparingInt(o -> o.contains(3) ? 0 : 1);
比较器c=比较器。比较器(o->o.contains(3)?0:1); 我建议使用新方法
Comparator.comparingX
。除了减少冗长之外,它还使编写正确的
compare
方法变得更加容易

这是可行的,
Collections.sort的文档保证了这一点

这种排序保证是稳定的:相等的元素不会因排序而重新排序


另一种方法是迭代原始列表并形成两个单独的列表,一个包含包含
3
的列表,另一个包含不包含
3
的列表,然后在末尾使用
addAll
。这比使用
sort
O(n)
而不是
O(n log n)
)的方法具有更好的时间复杂度。

问题是您没有返回
1
。您需要对比较中的所有案例进行说明。所以你应该做的是

Comparator<List<Integer>> c = new Comparator<List<Integer>>() {
    @Override
    public int compare(final List<Integer> o1, final List<Integer> o2) {
        System.out.println("Compare " + o1 + "<=>" + o2);
        if (o1.contains(3) && !o2.contains(3)) {
            return -1;
        } else if (!o1.contains(3) && o2.contains(3)) {
            return 1;
        } else {
            return 0;
        }
    }
};
比较器c=新比较器(){ @凌驾 公共整数比较(最终列表o1,最终列表o2){ System.out.println(“比较”+o1+“”+o2); 如果(o1.包含(3)和&!o2.包含(3)){ 返回-1; }否则,如果(!o1.包含(3)和&o2.包含(3)){ 返回1; }否则{ 返回0; } } };
更新

为了证明您的比较在某些情况下不起作用,请将
x
设为包含3的列表,将
y
设为不包含3的列表。根据您的比较,我们将
sgn(compare(x,y))
作为负值,将
sgn(compare(y,x))
作为零,从而使
比较器的第一个条件无效

此外,您不能有任何类型的传递性,因为您的比较永远不会返回正值。这使合同的其他两个条件无效


通常,您希望尽可能考虑所有情况。这将确保代码稳定。

如果要保留顺序,请尝试以下初始数据:

 final ArrayList<ArrayList<Integer>> arrayLists = Lists.newArrayList(
    Lists.newArrayList(3, 1, 2),        
    Lists.newArrayList(1, 2),
    Lists.newArrayList(1, 2, 3),
    Lists.newArrayList(1, 2),
    Lists.newArrayList(1, 3),
    Lists.newArrayList(1, 4),
    Lists.newArrayList(1, 2, 3)
);

您的比较器总是将包含3值的列表放在顶部,但它不能确保保留顺序。要做到这一点,您必须实现文档中所述的对称比较。

根据文档中提到的约束条件,有一些改进比较器或使比较器有效的建议

然而,实际问题是

是否有任何方法可以证明上述比较器不适用于某个特定的示例列表(其中不将包含3的列表放在顶部)

答案是:是的,有

这可能不是你在这个问题背后的真实意图。但一个非常务实的检查可能是这样的:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class ComparatorTest
{
    public static void main(String[] args)
    {
        List<List<Integer>> arrayLists = new ArrayList<List<Integer>>(
            Arrays.asList(
            Arrays.asList(1, 2),
            Arrays.asList(1, 2, 3),
            Arrays.asList(1, 2),
            Arrays.asList(1, 3),
            Arrays.asList(1, 4),
            Arrays.asList(1, 2, 3)
        ));
        System.out.println(arrayLists);
        Comparator<List<Integer>> c = new Comparator<List<Integer>>() {
            @Override
            public int compare(final List<Integer> o1, final List<Integer> o2) {
                //System.out.println("Compare " + o1 + "<=>" + o2);
                if (o1.contains(3))
                    return -1;
                return 0;
            }
        };

        Collections.sort(arrayLists, c);
        System.out.println(arrayLists);        

        validate(arrayLists, c);;
    }

    private static <T> void validate(
        List<T> list, Comparator<? super T> comparator)
    {
        for (int i=0; i<list.size(); i++)
        {
            for (int j=0; j<list.size(); j++)
            {
                T x = list.get(i);
                T y = list.get(j);
                int xy = comparator.compare(x, y);
                int yx = comparator.compare(y, x);

                if (Math.signum(xy) != -Math.signum(yx))
                {
                   System.out.println(
                       "The implementor must ensure that " +
                       "sgn(compare(x, y)) == -sgn(compare(y, x)) " +
                       "for all x and y.");
                   System.out.println("This is not the case for x="+x+", y="+y);
                }
                for (int k=0; k<list.size(); k++)
                {
                    T z = list.get(k);
                    int yz = comparator.compare(y, z);
                    int xz = comparator.compare(x, z);
                    if (xy > 0 && yz > 0)
                    {
                        if (xz <= 0)
                        {
                            System.out.println(
                                "The implementor must ensure that " +
                                "the relation is transitive: " +
                                "((compare(x, y)>0) && (compare(y, z)>0)) " +
                                "implies compare(x, z)>0.");
                            System.out.println(
                                "This is not the case for " +
                                "x="+x+", y="+y+", z="+z);
                        }
                    }

                    if (xy == 0)
                    {
                        if (Math.signum(xz) != -Math.signum(yz))
                        {
                            System.out.println(
                                "Ihe implementor must ensure that " +
                                "compare(x, y)==0 implies that " +
                                "sgn(compare(x, z))==sgn(compare(y, z)) " +
                                "for all z");
                            System.out.println(
                                "This is not the case for " +
                                "x="+x+", y="+y+", z="+z);
                        }
                    }
                }
            }
        }
    }
}

列出给定比较器和给定列表的所有违反合同的情况。

如果(o1.contains(3)和&&!o2.contains(3))返回-1,否则返回0如何?
以及另一种情况,即3在o2中,但不在o1中,返回1.BTW,此时列表包含一些重复元素。你确定这些不交换位置吗?还有,问题到底是什么?这种排序是否稳定,如何证明这一点,或者如何使其更好?想象两个列表,一个包含元素
3
(x),一个包含元素
2
(y),如果调用
compare(x,y)
,您将得到结果
-1
。如果你调用
比较(y,x)
你会得到结果
[[1, 2, 3], [1, 3], [1, 2, 3], [3, 1, 2], [1, 2], [1, 4]]
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class ComparatorTest
{
    public static void main(String[] args)
    {
        List<List<Integer>> arrayLists = new ArrayList<List<Integer>>(
            Arrays.asList(
            Arrays.asList(1, 2),
            Arrays.asList(1, 2, 3),
            Arrays.asList(1, 2),
            Arrays.asList(1, 3),
            Arrays.asList(1, 4),
            Arrays.asList(1, 2, 3)
        ));
        System.out.println(arrayLists);
        Comparator<List<Integer>> c = new Comparator<List<Integer>>() {
            @Override
            public int compare(final List<Integer> o1, final List<Integer> o2) {
                //System.out.println("Compare " + o1 + "<=>" + o2);
                if (o1.contains(3))
                    return -1;
                return 0;
            }
        };

        Collections.sort(arrayLists, c);
        System.out.println(arrayLists);        

        validate(arrayLists, c);;
    }

    private static <T> void validate(
        List<T> list, Comparator<? super T> comparator)
    {
        for (int i=0; i<list.size(); i++)
        {
            for (int j=0; j<list.size(); j++)
            {
                T x = list.get(i);
                T y = list.get(j);
                int xy = comparator.compare(x, y);
                int yx = comparator.compare(y, x);

                if (Math.signum(xy) != -Math.signum(yx))
                {
                   System.out.println(
                       "The implementor must ensure that " +
                       "sgn(compare(x, y)) == -sgn(compare(y, x)) " +
                       "for all x and y.");
                   System.out.println("This is not the case for x="+x+", y="+y);
                }
                for (int k=0; k<list.size(); k++)
                {
                    T z = list.get(k);
                    int yz = comparator.compare(y, z);
                    int xz = comparator.compare(x, z);
                    if (xy > 0 && yz > 0)
                    {
                        if (xz <= 0)
                        {
                            System.out.println(
                                "The implementor must ensure that " +
                                "the relation is transitive: " +
                                "((compare(x, y)>0) && (compare(y, z)>0)) " +
                                "implies compare(x, z)>0.");
                            System.out.println(
                                "This is not the case for " +
                                "x="+x+", y="+y+", z="+z);
                        }
                    }

                    if (xy == 0)
                    {
                        if (Math.signum(xz) != -Math.signum(yz))
                        {
                            System.out.println(
                                "Ihe implementor must ensure that " +
                                "compare(x, y)==0 implies that " +
                                "sgn(compare(x, z))==sgn(compare(y, z)) " +
                                "for all z");
                            System.out.println(
                                "This is not the case for " +
                                "x="+x+", y="+y+", z="+z);
                        }
                    }
                }
            }
        }
    }
}
The implementor must ensure that sgn(compare(x, y)) == -sgn(compare(y, x)) for all x and y.
This is not the case for x=[1, 2, 3], y=[1, 2, 3]
The implementor must ensure that sgn(compare(x, y)) == -sgn(compare(y, x)) for all x and y.
This is not the case for x=[1, 2, 3], y=[1, 3]
...
Ihe implementor must ensure that compare(x, y)==0 implies that sgn(compare(x, z))==sgn(compare(y, z)) for all z
This is not the case for x=[1, 2], y=[1, 2, 3], z=[1, 2, 3]
Ihe implementor must ensure that compare(x, y)==0 implies that sgn(compare(x, z))==sgn(compare(y, z)) for all z
This is not the case for x=[1, 2], y=[1, 2, 3], z=[1, 3]
...