[英]Java 8: Idiomatically creating a Comparator for ordering objects based on their index in a List
在Java 8中创建Comparator<T>
实例的最惯用的方法是什么,它根据给定List
的相对索引定义对象的排序,但是仍然将该列表中不存在的对象定义为“在”之后的那些对象。在列表中? - 如果我只使用List.indexOf(Object)
,那么不在列表中的对象将始终位于列表中的对象之前 ,因为对于不在列表中的所有对象都返回-1
,这比任何“true”索引都小:
final List<String> ordering = Arrays.asList("foo", "bar");
final Comparator<String> orderingComparator = Comparator.comparingInt(ordering::indexOf);
final String str1 = "foo";
final String str2 = "baz";
final String msg;
final int cmp = orderingComparator.compare(str1, str2);
if (cmp < 0) {
msg = String.format("Element \"%s\" is ordered before \"%s\".", str1, str2);
} else if (cmp > 0) {
msg = String.format("Element \"%s\" is ordered after \"%s\".", str1, str2);
} else {
msg = String.format("Element \"%s\" is equal to \"%s\".", str1, str2);
}
System.out.println(msg);
这打印
元素“foo”在“baz”之后排序。
而我希望的行为会打印出来
元素“foo”在“baz”之前订购。
您可以将indexOf
的结果威胁为无符号整数。 然后-1
将是最大值并放在其他值之后。
这可能是最可读的方法(尽管每个索引都被装箱):
Comparator.comparing(ordering::indexOf, Integer::compareUnsigned)
这是一个避免装箱的更快的替代方案:
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));
现在您的代码打印:
元素“foo”在“baz”之前订购。
在这种形式下,它调用indexOf()
两次是低效的。 如果您担心,我会留给您重写它以避免这种情况。
PS我将comparing()
更改为comparingInt()
。
提到Integer.compareUnsigned
给Bubletan +1。 如前所述,带有“下游”比较器的Comparator.comparing
的重载仅消耗引用类型,这会导致装箱开销。 使用的替代comparingInt
避免了开销,但它意味着你必须做算术的一点点来获得无符号比较的效果。 另一种方法是为比较器写出一个lambda,这不是太糟糕。 在这里,它包含在比较器返回函数中:
static <T> Comparator<T> comparingByIndex(List<? extends T> ordering) {
return (t1, t2) -> Integer.compareUnsigned(ordering.indexOf(t1),
ordering.indexOf(t2));
}
由于Collections.sort
和Stream.sorted
提供稳定的排序,因此ordering
列表中不存在的元素将以与它们在输入中出现的顺序相同的结尾。 这可能不是你想要的。 如果您希望它们按其他顺序排序,那么变体就是提供一个辅助比较器,当两个元素都不存在时调用它们:
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()))
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.