背景:现代 Java(最新版本;撰写此问题时为 JDK23)
给定两个可比较的对象(实现Comparable<T>
,其中 T 是它们共享的类型),如何获取两者中最大的一个?
如果这个问题是关于整数的,那么答案就相当简单:
int largest = Math.max(a, b);
这有点烦人(但也许从 varargs 引入新堆分配数组的角度来看,出于性能考虑是合理的),这只适用于恰好 2 个整数。
但是没有max
for <T>
。 它可能存在;这很容易:
public static <T implements Comparable<? super T>> T max(T a, T b) {
return a.compareTo(b) < 0 ? b : a;
}
但是,java.lang.Math
不包含这种方法;只有max
来自原始的方法。
并发症:
- 如果您可以拥有多于 2 个值,那就太好了,例如,签名为:
<T> T max(T... inputs)
。 - 如果不采用自然可比的数字,而是允许指定自定义比较器,那就太好了。
本着这种精神,我期望这种方法存在于java.util.Comparator
:
public default T max(T... inputs) {
if (inputs.length == 0) throw new IllegalArgumentException("inputs is empty"); // [1]
T out = inputs[0];
for (int i = 1; i < inputs.length; i++) {
T t = inputs[i];
if (out.compareTo(t) < 0) out = t;
}
return out;
}
java.util.Comparator
然而,在( ,, )java.util.Comparators
中我们期望的所有类中java.util.Comparable
似乎都没有这种方法。
检查我的一些代码项目,我发现有很多分散的方法,例如:
public LocalDate latest(LocalDate a, LocalDate b) {
return a.isBefore(b) ? b : a;
}
所有这些都可以用这种方法来替代;LocalDate
实现 Comparable,所以如果max
方法存在,我就可以调用它来max(date1, date2)
代替。
它存在于java.*
核心库的某个地方吗? 将来的 JDK 版本中是否有计划提供这种方法?
不要求特定的库,只是各种各样的清单:有没有包含它的流行库,像这个问题中的片段一样通用?2
[1] 设计师最初可能会认为Optional<T>
在这里返回一个更好;但这是错误的设计。调用者几乎总是会传递显式参数,从而保证Optional.none 绝对不会发生。出于同样的原因,让一个方法声明抛出文档中说在常见情况下永远不会发生的已检查异常是一种糟糕的 API 设计,让一个 API 返回一个永远不会发生的可选项NONE
同样令人讨厌。因此,异常在这里是正确的解决方案。
[2]null
处理方式在此有待商榷。如果一个人认同 SQL 的观点null
(即代表未知值),那么返回ifnull
的实现(例如,如果是)是不正确的(您如何知道,在已知值和未知值之间,已知值是较大的值?):NPE 是正确的。但是,如果一个人认同“缺失”的观点,那么 NPE 就是错误的,而返回非空值中的最大值是正确的。这个问题本身就够复杂了;我们假设任何合理的处理都是可以接受的。b
a
null
null
null
Java 标准库中已经提供了两种非常简单的方法,而且似乎不需要更多方法。所有这些都有替代
min
版本。Stream.of(inputs).max(Comparator.naturalOrder())
,它接受任意一个Comparator
并返回一个Optional
,如果集合为空,则返回空。如果是最大元素,NullPointerException
则抛出。null
Collections.max(Arrays.asList(inputs))
,它执行正确的类型安全操作(强制您传递Comparator
,或自然具有Comparable
元素),但返回一个元素或抛出NoSuchElementException
。null
如果输入包含 null 并且提供的比较器报告最大元素是 ,则将返回null
。既然你问到图书馆,我就在这里提一下 Guava 的产品:
Ordering
,这是 Guava 的“流畅Comparator
”类型,具有max
接受一个Iterable
、Iterator
或两个参数的方法。Ordering
并未完全弃用,但目前基本上已经过时了。Comparators
有一个max
方法可以接受两个Comparable
参数,或者两个参数和一个Comparator
。