SortedMap和TreeMap

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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
HashMap和TreeMap是Java中两种常用的Map实现类,它们之间有以下区别[^1][^2]: 1. 排序性能:TreeMap是有序的,而HashMap是无序的。TreeMap实现了SortedMap接口,可以根据键的自然顺序或自定义比较器进行排序。而HashMap则没有排序功能。 2. 时间复杂度:HashMap的基本操作(如get()和put())具有O(1)的恒定时间复杂度,而TreeMap的基本操作具有O(log n)的时间复杂度。这是因为HashMap使用哈希表实现,而TreeMap使用红黑树实现。 3. 迭代顺序:TreeMap的迭代顺序是按照键的顺序进行的,而HashMap的迭代顺序是不确定的,取决于哈希表的存储情况。 4. 空间占用:TreeMap的空间占用比HashMap更大,因为TreeMap需要额外存储红黑树的结构。 5. 线程安全性:HashMap是非线程安全的,而TreeMap可以通过Collections.synchronizedSortedMap()方法进行同步,使其成为线程安全的。 6. 适用场景:由于HashMap具有更好的性能和较低的空间占用,通常在大多数情况下优先选择HashMap。而TreeMap适用于需要有序存储键值对的场景。 下面是一个示例,演示了HashMap和TreeMap的区别: ```java import java.util.HashMap; import java.util.TreeMap; public class MapExample { public static void main(String[] args) { // 使用HashMap HashMap<String, Integer> hashMap = new HashMap<>(); hashMap.put("apple", 3); hashMap.put("banana", 2); hashMap.put("orange", 5); System.out.println("HashMap: " + hashMap); // 使用TreeMap TreeMap<String, Integer> treeMap = new TreeMap<>(); treeMap.put("apple", 3); treeMap.put("banana", 2); treeMap.put("orange", 5); System.out.println("TreeMap: " + treeMap); } } ``` 输出结果: ``` HashMap: {orange=5, banana=2, apple=3} TreeMap: {apple=3, banana=2, orange=5} ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值