SortedMap和TreeMap

来源:互联网 发布:优化——烙饼教学设计 编辑:程序博客网 时间:2024/06/05 08:59

1.接口SortedMap<K,V>

public interface SortedMap<K,V>
extends Map<K,V>
 保证按照键的升序(或降序)排列的映射,可以按照键的自然顺序(按照 Comparable 接口的实现类中的定义来排序,其中只有一个方法compareTo())进行排序, 或者通过创建有序映射时提供的比较器(通过Comparator对象)进行排序。对有序映射的集合视图

 (由 entrySet、keySet 和 values 方法返回)进行迭代时,此顺序就会反映出来。


 要采用此排序,还需要提供一些其他操作(此接口是相似于 SortedSet 接口的映射)。

 插入有序映射的所有键都必须实现 Comparable 接口(或者被指定的比较器所接受,即在Comparator进行键的大小比较时,不会产生ClassCastException)。


 另外,所有这些键都必须是可互相比较的:k1.compareTo(k2)(或 comparator.compare(k1, k2))
 对有序映射中的任意两个元素 k1 和 k2 都不得抛出 ClassCastException。

 试图违反这些限制将导致违反方法或者构造方法的调用,从而抛出 ClassCastException。


 注意,如果有序映射(SortedMap)正确实现了 Map 接口,则有序映射所保持的顺序(无论是否明确提供了比较器)都必须保持相等一致性。

 (相等一致性 的精确定义请参阅 Comparable 接口或 Comparator 接口,相等一致性即判断相等的方式与返回值是相同的,相等都是返回0,大于或小于分别返回1和-1)。 这也是因为 Map 接口是按照 equals 操作定义的,但有序映射使用它的 compareTo(或 compare)方法对所有键进行比较,

 因此有序映射的观点来看,此方法认为两个对象拥有相等的映射键则说明它们是相等的


 即使顺序没有保持相等一致性,树映射的行为仍然是 定义良好的,只不过没有遵守 Map 接口的常规协定。
 所有通用有序映射实现类(TreeMap是SortedMap的自带的唯一实现类,因此以TreeMap为例)都应该提供 4 个“标准”构造方法

 1) void(不带参数)构造方法,创建空的有序映射,按照键的自然顺序 排序。

public TreeMap() {        comparator = null;}


//TreeMap自身没有定义比较器comparator且没有实现Comparable,因此按照键的自然顺序排序,键的自然顺序是什么?即键所属的类应该实现了Comparable接口,否则无法实现排序

 2) 带有一个 Comparator 类型参数的构造方法,创建一个空的有序映射,根据指定的比较器排序。

    public TreeMap(Comparator<? super K> comparator) {        this.comparator = comparator;    }


//TreeMap指定了一个Comparator对象作为自身的比较器,其中的compare()会对TreeMap中的键进行比较

 3) 带有一个 Map 类型参数的构造方法,创建一个键-值映射关系与参数相同的有序映射,按照键的自然顺序排序。

    public TreeMap(Map<? extends K, ? extends V> m) {        comparator = null;        putAll(m);    }


//将一个普通的Map实现类对象(没有按键排序),进行键的自然顺序的排序,以键所属类中实现的compareTo()为排序依旧(Comparable接口的实现)

 4) 带有一个有序映射类型参数的构造方法,创建一个新的有序映射,键-值映射关系及排序方法与输入的有序映射相同。如TreeMap(SortedMap<K,? extends V> m) 

    public TreeMap(SortedMap<K, ? extends V> m) {        comparator = m.comparator();        try {            buildFromSorted(m.size(), m.entrySet().iterator(), null, null);        } catch (java.io.IOException cannotHappen) {        } catch (ClassNotFoundException cannotHappen) {        }    }

//上述直接传入了一个SortedMap的实现类对象m,那么m中一定实现了某种排序方式,所以将m中的Comparator对象赋给自身的Comparator对象,键值对直接复制就可以。

 除了 JDK 实现(TreeMap 类)遵循此建议外,无法保证强制实施此建议(因为接口不能包含构造方法)。

2.TreeMap<K,V>

 TreeMapSortedMap接口的基于红黑树的实现。此类保证了映射按照升序顺序排列关键字, 根据使用的构造方法不同,可能会按照键的类的自然顺序进行排序(参见 Comparable), 或者按照创建时所提供的比较器进行排序
 此实现为 containsKey、get、put 和 remove 操作提供了保证的 log(n) 时间开销。
 这些算法是 Cormen、Leiserson 和 Rivest 的《Introduction to Algorithms》中的算法的改编。
 注意,如果有序映射要正确实现 Map 接口,则有序映射所保持的顺序(无论是否明确提供比较器)都必须保持与等号一致。
(请参见与等号一致 的精确定义的 Comparable 或 Comparator。)这也是因为 Map 接口是按照等号操作定义的,
 但映射使用它的 compareTo(或 compare)方法对所有键进行比较,因此从有序映射的观点来看,
 此方法认为相等的两个键就是相等的。即使顺序与等号不一致,有序映射的行为仍然是 定义良好的;
 只不过没有遵守 Map 接口的常规约定。
 注意,此实现不是同步的。如果多个线程同时访问一个映射,并且其中至少一个线程从结构上修改了该映射,
 则其必须 保持外部同步。(结构上修改是指添加或删除一个或多个映射关系的操作;
 仅改变与现有键关联的值不是结构上的修改。)这一般通过对自然封装该映射的某个对象进行同步操作来完成。
 如果不存在这样的对象,则应该使用 Collections.synchronizedMap 方法来“包装”该映射。
 最好在创建时完成这一操作,以防止对映射进行意外的不同步访问,如下所示:
     Map m = Collections.synchronizedMap(new TreeMap(...));
 由所有此类的“collection 视图方法”所返回的迭代器都是快速失败的:在迭代器创建之后,
 如果从结构上对映射进行修改,除非通过迭代器自身的 remove 或 add 方法,其他任何时间任何方式的修改,
 迭代器都将抛出 ConcurrentModificationException。因此,面对并发的修改,迭代器很快就完全失败,
 而不是冒着在将来不确定的时间任意发生不确定行为的风险。
 注意,迭代器的快速失败行为无法得到保证,因为一般来说,不可能对是否出现不同步并发修改做出任何硬性保证。
 快速失败迭代器会尽最大努力抛出 ConcurrentModificationException。
 因此,为提高这类迭代器的正确性而编写一个依赖于此异常的程序是错误的做法:迭代器的快速失败行为应该仅用于检测 bug。
注意1:此实现不是同步的。不是线程安全的。
注意2:TreeMap是用键来进行升序顺序来排序的。通过Comparable 或 Comparator来排序。
注意3:由所有此类的“collection 视图方法”所返回的迭代器都是快速失败的。
注意4:和HashMap一样,如果插入重复的元素,后面的元素会覆盖前面的。
注意5: 键不可以为null,但是值可以为null

0 0