Java中的Map【二】SortedMap接口

        所使用的jdk版本为1.8版本,先看一下SortedMap在JDK中Map的UML类图中的位置:

2.1.2 SortedMap接口

       SortedMap<K,V>继承Map<K,V>接口,是一个更进一步提供基于键key的总体排序的 Map。该映射是根据其键key的自然顺序(自然排序的定义可以参见java.lang.Comparable接口,就是实现该接口)进行排序的,或者根据通常在创建有序映射SortedMap时提供的 Comparator 进行排序。

       插入有序映射的所有键都必须实现Comparable接口(或者被指定的比较器Comparator接受)。另外,所有这些键都必须是可互相比较的:对有序映射中的任意两个键k1和k2,执行k1.compareTo(k2)(或comparator.compare(k1, k2))都不得抛出ClassCastException。试图违反此限制将导致有问题的的方法或者构造方法调用抛出ClassCastException。

       SortedMap<K,V>接口从定义来看比较简单,需要注意的就是其中的key要么实现Comparable接口要么被指定的比较器Comparator接受。接口定义描述建议所有的SortedMap实现类都应该提供四个标准的构造函数:

(1)void 无参的构造函数,此时按照键的自然顺序进行排序;

(2)只有一个 Comparator 类型参数的构造方法,它创建一个空的有序映射,并且根据指定的 Comparator进行排序;

(3)只有一个 Map 类型参数的构造方法,它创建一个新的有序映射,其键-值映射关系与参数Map相同,按照键的自然顺序进行排序;

(4)只有一个 SortedMap 类型参数的构造方法,它创建一个新的有序映射,其键-值映射关系和排序方法与入参的SortedMap相同。

因为接口中无法定义构造方法,无法保证以上四种方法一定就会被实现。最上面的图,SortedMap实现类TreeMap是全部实现了这四个构造方法的。

因为SortedMap定义相对简单,简单列一些里面的函数:

public interface SortedMap<K,V> extends Map<K,V> {
    /**
     * 返回对此映射中的键进行排序的比较器;如果使用的自然顺序,返回null
     */
    Comparator<? super K> comparator();

    /**
     * 返回此映射的部分视图,其键值的范围从 fromKey(包括)到 toKey(不包括)。(如果 fromKey 
     * 和 toKey 相等,则返回映射为空。)返回的映射受此映射支持,所以在返回映射中的更改将反映在此 
     * 映射中,反之亦然。返回的映射支持此映射支持的所有可选映射操作。
     * 如果试图在返回映射的范围之外插入键,则返回的映射将抛出 IllegalArgumentException。
     *
     * @param fromKey - 返回映射中key的低端点(包括)
     * @param toKey - 返回映射中键的高端点(不包括)
     * @return 此映射的部分视图,其键值的范围从 fromKey(包括)到 toKey(不包括)
     * @throws ClassCastException - 如果无法使用此映射的比较器(如果此映射没有比较器,则使用 
     * 自然顺序)比较 fromKey 和 toKey。如果 fromKey 或 toKey 不能与映射中当前键进行比较,则 
     * 实现可以(但不是必须)抛出此异常。
     * @throws NullPointerException - 如果 fromKey 或 toKey 为 null,并且此映射不允许使 
     * 用 null 键
     * @throws IllegalArgumentException - 如果 fromKey 大于 toKey;如果此映射本身有范围限 
     * 制,并且 fromKey 或 toKey 位于范围的边界之外
     */
    SortedMap<K,V> subMap(K fromKey, K toKey);

    /**
     * 返回此映射的部分视图,其键值严格小于 toKey。返回的映射受此映射支持,所以在返回映射中的更改 
     * 将反映在映射中,反之亦然。返回的映射支持此映射支持的所有可选映射操作。
     *
     * 如果试图在返回映射的范围之外插入键,则返回的映射将抛出 IllegalArgumentException。
     *
     * @param toKey 返回映射视图中键的高端点(不包括)
     * @return 此映射的部分视图,且其中的键严格小于 toKey
     * @throws ClassCastException 如果 toKey 与此映射的比较器不兼容(或者,如果该映射没有比 
     * 较器, 如果 toKey 没有实现 Comparable)。如果 toKey 不能与映射中的当前键进行比较,则实 
     * 现可以(但不是必须)抛出此异常。
     * @throws NullPointerException 如果toKey为null,且该映射不允许key为null
     * @throws IllegalArgumentException 如果此映射本身有范围限制,并且 toKey 位于范围的边界 
     * 之外
     */
    SortedMap<K,V> headMap(K toKey);

    /**
     * 返回此映射的部分视图,其键大于等于 fromKey。返回的映射受此映射支持,所以在返回映射中的更改 
     * 将反映在映射中,反之亦然。返回的映射支持此映射支持的所有可选映射操作。
     * 如果试图在返回映射的范围之外插入键,则返回的映射将抛出 IllegalArgumentException。
     *
     * @param fromKey 返回映射中键的低端点(包括)
     * @return 此映射的部分视图,其中的键大于等于 fromKey
     * @throws ClassCastException 如果 fromKey 与此映射的比较器不兼容(或者,如果该映射没有 
     * 比较器;如果 fromKey 没有实现 Comparable)。如果 fromKey 不能与映射中的当前键进行比 
     * 较,则实现可以(但不是必须)抛出此异常。
     * @throws NullPointerException 如果 fromKey 为 null,并且此映射不允许使用 null 键
     * @throws IllegalArgumentException 如果此映射本身有范围限制,并且 fromKey 位于范围的 
     * 边界之外
     */
    SortedMap<K,V> tailMap(K fromKey);

    /**
     * 返回此映射中当前第一个(最低)键。
     *
     * @return 返回此映射中当前第一个(最低)键。
     * @throws NoSuchElementException 如果map为空(即size() == 0)
     */
    K firstKey();

    /**
     * 返回映射中当前最后一个(最高)键。
     *
     * @return 返回映射中当前最后一个(最高)键。
     * @throws NoSuchElementException 如果map为空(即size() == 0)
     */
    K lastKey();

    /**
     * 与父接口Map中的定义一样,但有顺序
     */
    Set<K> keySet();

    /**
     * 与父接口Map中的定义一样,但有顺序(根据key的顺序)
     */
    Collection<V> values();

    /**
     * 与父接口Map中的定义一样,但有顺序(根据key的顺序)
     */
    Set<Map.Entry<K, V>> entrySet();
}

需要注意的地方:

1、subMap(K fromKey, K toKey)、headMap(K toKey)、tailMap(K fromKey),都是满足fromKey <= key < toKey;

2、firstKey()和lastKey()方法,当SortedMap对象为空(size() == 0)就会抛出NoSuchElementException异常;

3、subMap等子集map和当前sortedMap的元素变更是互相影响的。

以上注意点可以用TreeMap实现验证(后续会分析在TreeMap中的实现):

public static void main(String[] args) {
        SortedMap<Integer, Integer> sortedMap = new TreeMap();
        sortedMap.put(2,2);
        sortedMap.put(4,4);
        sortedMap.put(1,1);
        System.out.println(sortedMap.subMap(1,2));
        System.out.println(sortedMap.headMap(2));
        System.out.println(sortedMap.tailMap(2));
        //获取的子集中添加一个元素会影响当前sortedMap
        SortedMap<Integer, Integer> subSortedMap = sortedMap.subMap(0,4);
        subSortedMap.put(3,3);
        //当前sortedMap增减元素会影响子集subSortedMap
        sortedMap.put(0,0);
        System.out.println(subSortedMap);
        System.out.println(sortedMap);
    }

 程序输出结果:

{1=1}
{1=1}
{2=2, 4=4}
{0=0, 1=1, 2=2, 3=3}
{0=0, 1=1, 2=2, 3=3, 4=4}

以上,SortedMap<K,V>暂时写这么多~

 

题外话:这里发现一个值得玩味的地方,就是keySet()、values()、entrySet()三个方法在父接口Map已经有相同结构的方法定义了,在SortedMap中又重新定义了一次(不过返回的都是有顺序的)。JDK中还有很多类似这样子接口重新定义了父接口中方法情况,比如List接口中定义了和父接口Collection一样的size()、isEmpty()等,感觉这种更多是处于软件工程和设计模式等方面的考虑吧,不知诸位对此有何看法,欢迎留言赐教~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章