Skip to content

Latest commit

 

History

History
98 lines (71 loc) · 4.36 KB

02_Maximum_of_a_Collection.md

File metadata and controls

98 lines (71 loc) · 4.36 KB

《《《 返回首页
《《《 上一节

集合的最大值

在本节中,我们将展示如何使用 Comparable <T> 接口来查找集合中的最大元素。我们将从一个简化版本开始。 在集合框架中找到的实际版本有一个更复杂一点的类 型签名,稍后我们将看到为什么。

以下代码可以从类 Collections 中找到非空集合中的最大元素:

public static <T extends Comparable<T>> T max(Collection<T> coll) {
   T candidate = coll.iterator().next();
   for (T elt : coll) {
     if (candidate.compareTo(elt) < 0) candidate = elt;
   }
   return candidate;
}

我们首先看到了在 1.4 节签名中声明新类型变量的泛型方法。 例如,asList 方法接受一个 E [] 类型的数组并返回 List <E> 类型的结果,并且对于任何 类型 E 都是这样。 这里我们有一个泛型方法来声明类型变量的边界。 方法 max 采用 Collection <T> 类型的集合并返回 T,并且它对任何类型 T 执行 此操作,使 T 成为 Comparable <T> 的子类型。

在类型签名开头的尖括号中突出显示的短语声明了类型变量 T,并且我们说 TComparable <T> 限定。 和通配符一样,即使边界是一个接口而不是一个类, 那么类型变量的边界总是用关键字 extends 来表示,就像这里的情况一样。 与通配符不同,类型变量必须始终使用 extends,而不是 super

在这种情况下,边界是递归的,因为 T 本身的边界取决于 T。 甚至可以有相互递归的界限,比如

<T extends C<T,U>, U extends D<T,U>>

9.5 节中会出现一个相互递归界限的例子。

方法主体选择集合中的第一个元素作为最大值的候选项,然后将候选项与集合中的每个元素进行比较,当元素较大时将候选项设置为元素。我们使用 iterator().next() 而不是 get(0) 获取第一个元素,因为 get 不是在列表以外的集合上定义的。当集合为空时,该方法引发 NoSuchElement 异常。

当调用方法时,T 可以选择为 Integer(因为 lteteger 实现 Comparable <Integer>)或 String(因为 String 实现了 Comparable<String>):

List<Integer> ints = Arrays.asList(0,1,2);
assert Collections.max(ints) == 2;
List<String> strs = Arrays.asList("zero","one","two");
assert Collections.max(strs).equals("zero");

但是我们可能不会选择 T 作为 Number(因为 Number 不能实现 Comparable):

List<Number> nums = Arrays.asList(0,1,2,3.14);
assert Collections.max(nums) == 3.14; // 编译报错

正如预期的那样,这里调用 max 是非法的。

这是一个效率提示。 前面的实现使用 foreach 循环来提高简洁性和清晰度。如果效率是一个紧迫的问题,您可能需要重写该方法以使用显式迭代器,如下所示:

public static <T extends Comparable<T>> T max(Collection<T> coll) {
    Iterator<T> it = coll.iterator();
    T candidate = it.next();
    while (it.hasNext()) {
        T elt = it.next();
        if (candidate.compareTo(elt) < 0) candidate = elt;
    }
    return candidate;
}

这分配一次迭代器而不是两次,并执行少一次比较。

方法的签名应该尽可能通用以最大化效用。 如果你可以用通配符替换一个类型参数,那么你应该这样做。 我们可以通过替换以下来改进 max 的签名:

<T extends Comparable<T>> T max(Collection<T> coll)

<T extends Comparable<? super T>> T max(Collection<? extends T> coll)

遵循获取和放置原则,我们使用 extends 来继承 Collection,因为我们从集合中获得类型 T 的值,并且我们使用 Comparablesuper,因为我们将 类型 T 的值放入 compareTo 方法中。 在下一节中,我们将看到一个不会键入的示例 - 如果上面的 super 子句被忽略。

如果您查看 Java 库中此方法的签名,您将看到一些内容看起来比上面的代码更糟糕:

<T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll)

这是为了向后兼容,正如我们将在 3.6 节结束时所解释的那样。

《《《 下一节
《《《 返回首页