[英]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.