1

从这个API

1)以下是 TreeMap 的构造函数,它接受另一个地图并使用(传递的地图的)可比较接口对其进行排序。

TreeMap

public TreeMap(Map<? extends K,? extends V> m)

Constructs a new tree map containing the same mappings as the given map, ordered according to the natural ordering of its keys. All

插入新地图的键必须实现 Comparable 接口。此外,所有此类键必须相互比较:k1.compareTo(k2) 不得为映射中的任何键 k1 和 k2 抛出 ClassCastException。此方法在 n*log(n) 时间内运行。

Parameters:
    m - the map whose mappings are to be placed in this map
Throws:
    ClassCastException - if the keys in m are not Comparable, or are not mutually comparable
    NullPointerException - if the specified map is null

2) 以下是 TreeMap 的构造函数,它接受另一个地图并使用(传递的地图的)比较器接口对其进行排序。

树状图

public TreeMap(SortedMap<K,? extends V> m)

Constructs a new tree map containing the same mappings and using the same ordering as the specified sorted map. This method runs in

线性时间。

Parameters:
    m - the sorted map whose mappings are to be placed in this map, and whose comparator is to be used to sort this map
Throws:
    NullPointerException - if the specified map is null

为什么第一个签名是public TreeMap(Map<? extends K, ? extends V> m),第二个是public TreeMap(SortedMap<K,? extends V> m)

更新:如果问题不够清楚,我想知道为什么与构造函数中的 KEYS 参数相关的泛型部分彼此不同。? extends KK

4

2 回答 2

2

构造Map函数使用? extends K它是因为让它尽可能松散总是好的。

SortedMap构造函数之所以使用是K因为它采用了另一个映射的比较器,并且如果边界是? extends K,则不会进行类型检查。

让我更详细地解释一下。

假设我们有类Base,Middle extends BaseDerived extends Middle implements Foo

然后我们有两个特殊的比较器,BaseComp implements Comparator<Base>FooComp implements Comparator<Foo>

现在我们创建一个TreeMap<Derived, String>并给它一个FooComp

SortedMap<Derived, String> sm1 = new TreeMap<Derived, String>(new FooComp());

TreeMap(Comparator<? super K> comp)请注意带有我们使用的签名的构造函数。FooComp实现Comparator<Foo>,并且因为FooDerived(我们的K)的超类,所以这个类型检查。

接下来,我们看一下这张地图:

SortedMap<Derived, String> sm2 = new TreeMap<Derived, String>(new BaseComp());

这也很好。BaseComp实现Comparator<Base>Base super Derived类型检查。

好的,让我们进一步假设采用排序地图的构造函数具有TreeMap(SortedMap<? extends K, ? extends V> map)与普通地图类似的签名。那么我们现在可以这样说:

SortedMap<Middle, String> sm3 = new TreeMap<Middle, String>(sm2);

然后构造函数将采用sm2' 比较器,它是 a BaseComp,这很好:Base super Middle仍然是类型检查。但是,我们也可以这样做:

SortedMap<Middle, String> sm4 = new TreeMap<Middle, String>(sm1);

构造函数采用sm1的比较器,它是 a FooComp,并且Foo super Middle是不正确的。因此,如果允许这样做,那将是不安全的。

但是,这是不允许的。特别是,假设您更改了构造函数的边界。我将为问号分配数字——不是有效的语法,但有助于理解。所以首先你有一个可以SortedMap访问比较器的接口:

public interface SortedMap<K1, V1> extends Map<K1, V1> {
    Comparator<?1 super K1> comparator();
}

然后你有这个TreeMap类和它的构造函数:

public class TreeMap<K2, V2> ... {
    private Comparator<?2 super K2> comp;

    public TreeMap(SortedMap<?3 extends K2, ?4 extends V2> map) {
        this.comp = map.comparator();
        // magic stuff to transfer elements
    }
}

好的,在我们的具体案例中,我们尝试这样做new TreeMap<Middle, String>((SortedMap<Derived, String>)x),所以将这些类型匹配起来:K1 = Derived, K2 = Middle. 我将忽略这些值,它们无关紧要。

构造函数的签名很好。?3推断为K1isDerivedDerived extends Middle类型检查。

构造函数的第一行是有趣的部分。的类型this.compComparator<?2 super Middle>map.comparator()根据SortedMap的定义的类型是?1 super K1,所以在我们的例子中是?1 super Derived

所以我们尝试将 a 分配?1 super Derived给 a ?2 super Middle。为了使它起作用,每个基本类型Derived也必须是 的基本类型Middle。但是我们已经展示了一个反例: ,但 notFoo实现的接口是一个。如果有一个和,那将是另一个。DerivedMiddleMiddle2MiddleDerived

所以编译器不能允许这个赋值,因此必须无法编译代码。

这很有趣!

于 2013-07-04T17:18:43.917 回答
1

两者都是有效的构造函数,但作为惯例,您应该始终使用您可以使用的最高接口。因此,虽然采用 Map 的构造函数在所有情况下都可以工作,但在从 SortedMap 构建 TreeMap 时,应该使用 SortedMap 构造函数。

但为什么在这种情况下?因为通过让构造函数知道您传递的不是常规地图,而是已经排序的地图,它可能会利用它并以最佳方式运行。在这种特殊情况下,很容易看出为什么从已排序的地图构建 TreeMap(这是一个排序的地图)比从无序地图(如 Hasmap)构建它要快:构造函数必须订购一个首先映射,但它会跳过 SortedMap 上的这一部分,因为它已经排序。

于 2013-07-04T15:04:17.497 回答