[英]Java 8: Idiomatically creating a Comparator for ordering objects based on their index in a List
What is the most idiomatic way of creating a Comparator<T>
instance in Java 8 which defines an ordering of objects based on their relative index in a given List
but nevertheless defines an object not present in that list as being "after" those which are in the list? 在Java 8中创建
Comparator<T>
实例的最惯用的方法是什么,它根据给定List
的相对索引定义对象的排序,但是仍然将该列表中不存在的对象定义为“在”之后的那些对象。在列表中? — if I simply use List.indexOf(Object)
, objects not in the list will always be before those in the list due the fact that -1
is returned for all objects not in the list, which is less than any "true" index: - 如果我只使用
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);
This prints 这打印
Element "foo" is ordered after "baz".
元素“foo”在“baz”之后排序。
whereas my desired behavior would print 而我希望的行为会打印出来
Element "foo" is ordered before "baz".
元素“foo”在“baz”之前订购。
You could threat the result of indexOf
as an unsigned integer. 您可以将
indexOf
的结果威胁为无符号整数。 Then -1
would be the maximum value and be placed after the others. 然后
-1
将是最大值并放在其他值之后。
This is probably the most readable way to do this (every index gets boxed though): 这可能是最可读的方法(尽管每个索引都被装箱):
Comparator.comparing(ordering::indexOf, Integer::compareUnsigned)
Here is a faster alternative that avoids boxing: 这是一个避免装箱的更快的替代方案:
Comparator.comparingInt(s -> ordering.indexOf(s) + Integer.MIN_VALUE)
I can only think of 我只能想到
final Comparator<String> orderingComparator
= Comparator.comparingInt(s -> ordering.indexOf(s) == -1 ? ordering.size() : ordering.indexOf(s));
Now your code prints: 现在您的代码打印:
Element "foo" is ordered before "baz".
元素“foo”在“baz”之前订购。
In this form it is inefficient in that it calls indexOf()
twice. 在这种形式下,它调用
indexOf()
两次是低效的。 If you're concerned, I leave it to you to rewrite it to avoid that. 如果您担心,我会留给您重写它以避免这种情况。
PS I changed comparing()
to comparingInt()
. PS我将
comparing()
更改为comparingInt()
。
+1 to Bubletan for mentioning Integer.compareUnsigned
. 提到
Integer.compareUnsigned
给Bubletan +1。 As noted there, the overload of Comparator.comparing
that takes a "downstream" Comparator consumes only reference types, which incurs boxing overhead. 如前所述,带有“下游”比较器的
Comparator.comparing
的重载仅消耗引用类型,这会导致装箱开销。 The alternative of using comparingInt
avoids that overhead, but it means you have to do a tiny bit of arithmetic to get the effect of unsigned comparison. 使用的替代
comparingInt
避免了开销,但它意味着你必须做算术的一点点来获得无符号比较的效果。 An alternative is to write out a lambda for a Comparator, which isn't too bad. 另一种方法是为比较器写出一个lambda,这不是太糟糕。 Here it is, wrapped in a Comparator-returning function:
在这里,它包含在比较器返回函数中:
static <T> Comparator<T> comparingByIndex(List<? extends T> ordering) {
return (t1, t2) -> Integer.compareUnsigned(ordering.indexOf(t1),
ordering.indexOf(t2));
}
Since Collections.sort
and Stream.sorted
provide stable sorting, the elements not present in the ordering
list will end up at the end, in the same order in which they occurred in the input. 由于
Collections.sort
和Stream.sorted
提供稳定的排序,因此ordering
列表中不存在的元素将以与它们在输入中出现的顺序相同的结尾。 This might not be what you want. 这可能不是你想要的。 If you want them sorted by some other order, then a variation would be to provide a secondary Comparator that's called when neither element is present:
如果您希望它们按其他顺序排序,那么变体就是提供一个辅助比较器,当两个元素都不存在时调用它们:
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);
}
};
}
If you're sorting a stream, these variations let you do things like the following: 如果您要对流进行排序,这些变体可让您执行以下操作:
.sorted(comparingByIndex(ordering))
.sorted(comparingByIndex(ordering, someSpecializedComparator))
.sorted(comparingByIndex(ordering, Comparator.reverseOrder()))
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.