如何在 Java 中实现空操作比较器?

How do I implement a no-op comparator in Java?

假设我有一个接受以下内容的库调用:

  1. 项目列表(mixed/multiple 类型T1T2T3、..)和
  2. 自定义比较器 C,

并且,除了做一些工作之外,还对它接受的列表进行排序(首先按类型,然后使用自定义比较器 C)。

现在,我需要 T1 类型的项目按 类型 排序,然后使用 自定义顺序 ,而项目所有其他类型(T2T3、..)仅按 type 排序(即,一旦按类型排序,因此仅使用空操作排序,保序比较器)。

换句话说,我需要这样的东西(T1java.lang.IntegerT2java.lang.String):

import java.util.Comparator;
import java.util.List;

import static java.util.Arrays.asList;
import static java.util.Comparator.comparing;
import static java.util.Comparator.comparingInt;

class C {
    /**
     * The library call.
     */
    static <T> void processAndSort(final List<T> items,
                       final Comparator<? super T> secondaryComparator) {
        final Comparator<T> typeComparator = comparing(it -> it.getClass().getName());

        items.sort(typeComparator.thenComparing(secondaryComparator));

        // Do some extra work.
    }

    public static void main(final String ... args) {
        final List<?> items = asList(13, "Lorem",
                         8, "ipsum",
                         5, "dolor",
                         3, "sit",
                         2, "amet",
                         1, "consectetur",
                         1, "adipiscing");

        processAndSort(items, (final var left, final var right) -> {
            final Class<?> clazz = left.getClass();

            /*
             * Should be already sorted by type.
             */
            if (!clazz.equals(right.getClass())) {
                throw new IllegalStateException();
            }

            if (clazz.equals(Integer.class)) {
                /*
                 * Compare integers using a custom comparator.
                 */
                return comparingInt(Integer::intValue).compare((Integer) left, (Integer) right);
            }

            /*
             * For all other types, retain the original order.
             */
            return 0;
        });

        System.out.println(items);
    }
}

上面的代码,当运行时,会产生如下输出:

[1, 1, 2, 3, 5, 8, 13, Lorem, ipsum, dolor, sit, amet, consectetur, adipiscing]

现在,如果 Merge Sort and Timsort algorithms (used by the JDK to sort non-primitives) are stable,我的自定义比较器是否可以将 return 0 用于应保留顺序的对?

还有其他选择吗?

是的,用这样的Comparator来排序List是可以的。

Collections.sort() (and List.sort() 非常相似的措辞)只需要

All elements in the list must be mutually comparable using the specified comparator (that is, c.compare(e1, e2) must not throw a ClassCastException for any elements e1 and e2 in the list).

继续

This sort is guaranteed to be stable: equal elements will not be reordered as a result of the sort.

Comparator 的合同也没有施加任何进一步的限制来禁止这种实施 - 它只是警告说这种顺序不“与平等一致”,因此不能用于某些地图或集合.

是的,但是。

两个重要问题:

  1. 是的,比较器有'on equal levels'的概念,意思是:对象在'same hashCode() and a.equals(b) is true'的意义上可能不一定相等,但比较器表明它们在同级别 (compare(a, b) == 0)。但是, 意味着什么 取决于使用比较器的东西。具体来说,对于 List.sort,这仅意味着 'at the same level' 的所有内容都将彼此相邻,此外,排序是稳定的,这意味着,无论他们在开始排序之前的顺序如何,都是他们得到的顺序然后。 但这正是 List 排序的作用 - 例如 TreeSet 声明您只能有 1 个这样的元素;如果比较器说 2 个项目在同一级别,就 TreeSet/TreeMap 而言,它们是相等的,因此一个树集中不能有 2 个项目其中 compare(a, b) == 0,即使 !a.equals(b)。因此,虽然这个问题的答案是 'yes',但请注意您不能普遍使用 'comparator says they are on the same level'.

  2. 比较器报告 compare(a, b) == 0 不等于 a/b 是完全可以接受的,但还有其他规则 not 供辩论。 所有 比较者必须 始终遵守这些规则;不这样做是一个问题,无论您将比较器用于什么目的:

  • compare(a, a) == 0 必须始终保持。
  • 如果 compare(a, b) < 0,则 compare(b, a) > 0 必须始终成立。
  • 如果compare(a, b) < 0compare(b, c) < 0,则compare(a, c) < 0必须成立。

这个比较器:

Comparator<Object> communism = (a, b) -> 0;

确实遵守所有这些规则,所以,当然,你可以做到。