简体   繁体   English

Java 8:惯用于创建Comparator,用于根据List中的索引对对象进行排序

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM