标准API中是否存在自然比较器?

61

我需要一个比较器作为策略模式的一部分,它可以使用对象的自然顺序或某些自定义顺序。对于自然排序的情况,我编写了一个简单的比较器:

private static class NaturalComparator<T extends Comparable<? super T>> implements Comparator<T> {
    @Override
    public int compare(T o1, T o2) {
        return o1.compareTo(o2);
    }
}

看起来很简单,但我想知道标准API中是否有这样一个类。我查看了TreeMap,并且它可以在没有这样一个类的情况下实现,因此当编写该代码时,显然答案是不需要,但也许后来添加了这个功能。


4
值得一提的是:除了标准 API 之外,Guava 的 Ordering.natural() 也是一个选择。 - Jon Onstott
5个回答

71

Java 8中添加到比较器

static <T extends Comparable<? super T>> Comparator<T> naturalOrder()

例如,可以像这样使用:

Comparator<Double> natural = Comparator.<Double>naturalOrder();
return natural.compare(1.0, 1.1));

15
如果你还没有开始使用Java 8,并且想在像Arrays.sort()Collections.sort()这样简单的情况下使用比较器(Comparator),你可以将null直接传递给Comparator参数,它会使用自然排序。但并不是所有使用比较器的情况都能保证这样运作。至少对于ArraysCollections来说,它确实可以这样工作。 - Joe
如果使用数组,则数组项的类必须实现Comparable接口。 - natinusala
Comparator.<String>naturalOrder().compare(o1, o2) 支持字母数字吗? - Alex78191

54

是的,JDK绝对有这个!在这里:

Collections.reverseOrder(Collections.reverseOrder())

只是开玩笑。(但这是真的。(永远不要真的使用它。))


5
我正在取消接受这个答案,因为它已经被 Java 8 所取代,但对于 Java 8 之前的版本来说是正确的。但实际上不要使用它 ;-). - Yishai
1
我可能没有听懂笑话。为什么我不能在旧版Java中使用它呢?这里并没有真正地反转任何东西。开销非常小。 - Jirka Hanika
6
@JirkaHanika,编码中最重要的事情是长期来看让代码易于理解。当然,这句话很简洁,并且只使用基于API的类,但问题中的代码对于任何中级及以上的Java开发人员来说一目了然。这种“技巧”会使后来者感到困惑,因为他们无法轻易地理解它。请记住,让代码易于阅读和理解是至关重要的。 - Yishai
@Yishai - 好的,我明白你的意思了。也许在旧的API中,这是最简单的自然比较器,但我可以使用API自己实现一个更好的比较器,同样容易地使用compareTo。谢谢。 - Jirka Hanika
1
Comparator naturalOrder = Collections.reverseOrder(reverseOrder);``` 这段代码对我来说很容易理解。 - Antoine Snyers

10

19
了解,Guava称其为Ordering.natural() - Kevin Bourrillion

2
我不熟悉Java中的默认比较器,但显然,Comparator到compareTo通常只是一个包装器。
标准API中没有“自然排序”的普遍定义,尽管某些内置类型,如数字,具有compareTo的实现,然后成为它们的自然排序。
TreeMap和TreeSet等所有这些都应该抛出RuntimeException,如果您放入的对象没有实现Comparable。因此,例如,您可以投入字符串或数字,但不能投入另一个集合。
如果没有可用的比较器,TreeMap的代码将不使用比较器-而是使用compareTo。要使用compareTo,它会进行到Comparable的转换,这是异常的来源。
    private int compare(K k1, K k2) {
      return (comparator==null ? ((Comparable <K>)k1).compareTo(k2)
                                : comparator.compare((K)k1, (K)k2));
  }

1
顺便提一下,在Java中,Comparable实现被称为自然排序:http://www.filigris.com/products/docflex_javadoc/examples/new/java/lang/Comparable.html#compareTo%28T%29 - Yishai
@Yishai:啊,我不知道这一点。我本来以为Java会像火一样避免使用这个术语,因为compareTo可能具有任意顺序,可能与数学自然排序或源域中的任何排序不匹配。(例如,字符串的排序在语言上并不是“自然”的,因为它不是纯自然语言顺序) - Uri

2

我认为,如果一个类有自然排序,那么在Java中实现Comparable而不是为每个类编写Comparator实现更加常见。

因此,如果涉及的对象具有定义的自然排序,则必须实现Comparable并定义compareTo方法。不需要寻找Comparator。java.util中的大多数类都可以使用可选的Comparator(如果有任何特定的排序要求),或者仅尝试在没有指定其他排序的情况下调用对象的compareTo方法。

所以,长话短说:每当您想对类强制执行自然排序时,请实现Comparable,只有在需要除自然排序之外的其他内容时才使用Comparator


1
@downvoter:请解释一下你给出的负评理由。我有可能犯了错误或者完全是错的,但如果我(或者原帖作者)不知道是哪里出了问题,这并没有什么帮助。 - MAK
1
MAK,虽然我没有投反对票,但我也没有投赞成票,因为它回避了问题。有时您希望一个类基于策略模式进行排序(在我的情况下,它是更大的字母数字排序的子排序),因此您不希望自然排序有单独的代码路径(TreeMap的代码相当丑陋,因为他们可能出于性能原因而这样做)。 - Yishai
我完全同意不附评论就给出负评的做法是不好的。 - Yishai
@Yishai:收到。所以,我的意思是:不,Collections类中没有默认的Comparator-因为我在答案中所述的原因。如果有需要,您必须自己创建。 - MAK

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接