Java 找出比较器可能无法工作的情况
我想对列表(整数列表)进行排序,这样包含数字3的列表就被放在列表的顶部,剩下的元素的现有顺序保持不变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
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]
...