Java8:习惯性地创建一个比较器,用于根据列表中的索引对对象进行排序

Java8:习惯性地创建一个比较器,用于根据列表中的索引对对象进行排序,java,list,java-8,comparator,Java,List,Java 8,Comparator,在Java 8中创建比较器实例的最惯用方法是什么?该实例根据给定列表中对象的相对索引定义对象的顺序,但将该列表中不存在的对象定义为“在”列表中的对象之后如果我只是简单地使用,列表中不存在的对象将始终位于列表中的对象之前,因为对于列表中不存在的所有对象都返回了-1,这小于任何“true”索引: final List ordering=Arrays.asList(“foo”、“bar”); 最终比较器orderingComparator=Comparator.comparingInt(orderin

在Java 8中创建
比较器
实例的最惯用方法是什么?该实例根据给定
列表中对象的相对索引定义对象的顺序,但将该列表中不存在的对象定义为“在”列表中的对象之后如果我只是简单地使用,列表中不存在的对象将始终位于列表中的对象之前,因为对于列表中不存在的所有对象都返回了
-1
,这小于任何“true”索引:

final List ordering=Arrays.asList(“foo”、“bar”);
最终比较器orderingComparator=Comparator.comparingInt(ordering::indexOf);
最后一个字符串str1=“foo”;
最终字符串str2=“baz”;
最终字符串msg;
final int cmp=orderingComparator.compare(str1,str2);
if(cmp<0){
msg=String.format(“元素\%s\”排序在\%s\”、str1、str2之前);
}否则如果(cmp>0){
msg=String.format(“元素\%s\”排列在\%s\”、str1、str2之后);
}否则{
msg=String.format(“元素\%s\”等于\%s\”,str1,str2);
}
System.out.println(msg);
这张照片

元素“foo”在“baz”之后排序

而我想要的行为会打印出来

元素“foo”在“baz”之前排序


您可以将
indexOf
的结果作为无符号整数进行威胁。然后,
-1
将是最大值,并放在其他值之后

这可能是最具可读性的方法(不过每个索引都会被装箱):

以下是一个避免装箱的更快的替代方案:

Comparator.comparingInt(s -> ordering.indexOf(s) + Integer.MIN_VALUE)
我只能想到

    final Comparator<String> orderingComparator 
            = Comparator.comparingInt(s -> ordering.indexOf(s) == -1 ? ordering.size() : ordering.indexOf(s));
最终比较器排序比较器
=Comparator.comparingit->ordering.indexOf=1?ordering.size():ordering.indexOf(s));
现在,您的代码将打印:

元素“foo”在“baz”之前排序

在这种形式中,调用
indexOf()
两次是低效的。如果你担心的话,我让你重写它来避免这种情况

PS我将
comparing()
改为
comparingit()

+1改为提及
整数。compareUnsigned
。如上所述,
Comparator.comparating
的重载只消耗引用类型,这会产生装箱开销。使用
comparingit
的替代方法可以避免这种开销,但这意味着您必须进行少量运算才能获得无符号比较的效果。另一种方法是为比较器写出一个lambda,这还不算太糟糕。在这里,它被包装在一个比较器返回函数中:

static <T> Comparator<T> comparingByIndex(List<? extends T> ordering) {
    return (t1, t2) -> Integer.compareUnsigned(ordering.indexOf(t1),
                                               ordering.indexOf(t2));
}
如果要对流进行排序,这些变体允许您执行以下操作:

    .sorted(comparingByIndex(ordering))

    .sorted(comparingByIndex(ordering, someSpecializedComparator))

    .sorted(comparingByIndex(ordering, Comparator.reverseOrder()))

不能直接重用现有比较器。直接写出逻辑——这并不难。这是一个旁白:你不能确定比较器会返回-1、0或1。只有
compare()
“返回一个负整数、零或正整数,因为第一个参数小于、等于或大于第二个参数。”只需编写
indexOf()
方法,当元素为absent@ThorbjørnRavnAndersen直接写出逻辑——这并不难——如果始终遵循这一建议,那么逻辑是否仍然可以直接焊接到硅上?@ThorbjørnRavnAndersen我同意我们需要抽象层,但事实上,我们可以将抽象应用于这个问题。区别在于它不是面向对象的抽象,而是通过功能组合进行的抽象。正如我的回答所示,通过将JDK中现有的比较函数与少量的粘合逻辑相结合,可以创建具有有趣行为的新比较器。这些问题/答案让我希望有某种协作回答系统(似乎甚至“Wiki”该功能不会因为你提供了一个好答案而给你分数,是吗?。@错误语言学家不太确定这里最好的东西是什么。虽然结果有些零碎,但多个答案加上相互向上投票(由徽章奖励)似乎涵盖了很多领域。没有什么动机去创建一个总结其他答案的维基,但创建一个总结其他答案的非维基答案似乎是剽窃。(但也许那只是我)meta的好话题?第二种方法,使用另一个
比较器
,已经过时了。您只需通过
然后比较
链接另一个比较器即可。当然,如果存在重复的
equal
对象,并且与重复的
列表的性能灾难相比,性能影响可以忽略不计,那么这也可能会调用
equal
对象的第二个比较器。indexOf
调用更大的列表。对于较小的列表,更复杂的解决方案就更不值得了…@Holger,虽然在这种情况下效果是相同的,但您所建议的内容实际上不会有一些不同的语义吗?--Stuart Marks的解决方案的意思是“如果
c1
c2
都不在
排序中
,请使用
cmp
对它们进行比较”,而您的解决方案将意味着“通过
排序中的索引比较
c1
c2
,如果索引相同,请将它们与链中的下一个
比较器
进行比较。”“。它们之所以在这里做相同的事情,是因为
List.indexOf(Object)
为给定
List
@erratlinguist中未包含的所有对象返回一个常量值(
-1
):有两种方法,索引可以是相同的。或者,它们都不在列表中,或者它们具有相同的索引,这意味着它们是相同的对象(per
等于
)。后者意味着任何合理的比较器都应该认为它们是一样的,所以它没有I。
static <T> Comparator<T> comparingByIndex(List<? extends T> ordering) {
    return (t1, t2) -> Integer.compareUnsigned(ordering.indexOf(t1),
                                               ordering.indexOf(t2));
}
static <T> Comparator<T> comparingByIndex(List<? extends T> ordering,
                                          Comparator<? super T> cmp) {
    return (t1, t2) -> {
        int c1 = ordering.indexOf(t1);
        int c2 = ordering.indexOf(t2);
        if (c1 == -1 && c2 == -1) {
            return cmp.compare(t1, t2);
        } else {
            return Integer.compareUnsigned(c1, c2);
        }
    };
}
    .sorted(comparingByIndex(ordering))

    .sorted(comparingByIndex(ordering, someSpecializedComparator))

    .sorted(comparingByIndex(ordering, Comparator.reverseOrder()))