Java 8:惯用于创建Comparator,用于根据List

时间:2016-12-13 13:51:34

标签: java list java-8 comparator

在Java 8中创建Comparator<T>实例的最惯用的方法是什么,它根据给定List中的相对索引定义对象的排序,但仍然定义了该列表中不存在的对象作为&#34;之后&#34;列表中 的那些? - 如果我只使用List.indexOf(Object),则列表中不包含的对象 之前的,因为对于不在列表中的所有对象返回-1这一事实,这比任何&#34;真&#34;指数:

    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);

打印

  

元素&#34; foo&#34;是在&#34; baz&#34;。

之后订购的

而我想要的行为会打印

  

元素&#34; foo&#34;在&#34; baz&#34;。

之前订购

3 个答案:

答案 0 :(得分:9)

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

这可能是最易读的方法(虽然每个索引都装箱了):

Comparator.comparing(ordering::indexOf, Integer::compareUnsigned)

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

Comparator.comparingInt(s -> ordering.indexOf(s) + Integer.MIN_VALUE)

答案 1 :(得分:4)

我只能想到

    final Comparator<String> orderingComparator 
            = Comparator.comparingInt(s -> ordering.indexOf(s) == -1 ? ordering.size() : ordering.indexOf(s));

现在你的代码打印出来了:

  

元素“foo”在“baz”之前订购。

在这种形式下,它调用indexOf()两次是低效的。如果你担心,我会留给你重写它以避免这种情况。

PS我将comparing()更改为comparingInt()

答案 2 :(得分:2)

+1到Bubletan提及Integer.compareUnsigned。如前所述,Comparator.comparing的过载需要&#34;下游&#34;比较器仅消耗引用类型,这会导致拳击开销。使用comparingInt的替代方法可以避免这种开销,但这意味着您必须执行一些算术来获得无符号比较的效果。另一种方法是为比较器写出一个lambda,这不是太糟糕。在这里,它包含在比较器返回函数中:

static <T> Comparator<T> comparingByIndex(List<? extends T> ordering) {
    return (t1, t2) -> Integer.compareUnsigned(ordering.indexOf(t1),
                                               ordering.indexOf(t2));
}

由于Collections.sortStream.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()))