繁体   English   中英

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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