Java中的Map【一】Map接口

一 JDK中的Map继承实现关系 

       不经意间看了Java中LinkedHashMap和LinkedHashSet的源码实现,觉得一些地方还是挺有意思的。之前阅读过一些,但没有进行系统性地总结,打算尝试一下Map源码的系统性整理学习。因为Java中的Set底层基本上是借助对应的Map实现的,故Set打算放在Map之后学习。所使用的jdk版本为1.8版本,先看一下JDK中Map的UML类图:

       这张图囊括了JDK中绝大部分的Map(没有将java.util.Collections中的一些private static map结构、java.util.EnumMap等列入),后面逐一分析,希望能有所收获。

二 源码分析学习

2.1 相关接口

2.1.1 Map接口

     Map<K,V>是最基本的接口,它表示将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值。此接口被设计用来取代 java.util.Dictionary 类,后者完全是一个抽象类,而不是一个接口。因为Map涉及到Key和Value两个对象,所以它不像List<E>或者Set<E>接口都实现了Collection<E>接口,Map<K,V>没有继承任何接口。有些Map的实现可以保证顺序,如TreeMap;有些Map的实现不保证顺序,如HashMap;还有特殊的Map实现比如LinkedHashMap具有可预知的迭代顺序,该迭代顺序可以是插入顺序或者是访问顺序。

       下面列一些需要注意的地方:

2.1.1.1 containsKey(Object key)方法

      Map中需要注意的containsKey(Object key)方法,Map是否已经包含有某个对象key的判断条件:当且仅当此映射包含针对满足 (key==null ? k==null : key.equals(k)) 的键 k 的映射关系时,返回 true。(最多只能有一个这样的映射关系)。可能日常开发中常用字符串String作为key,而String已经帮我们实现了Object类中的equals()方法和hashCode()方法,所以当我们使用自定义的对象作为key时,一定要考虑这两个方法的实现。containsKey(Object key)定义如下:

    /**
     * Returns <tt>true</tt> if this map contains a mapping for the specified
     * key.  More formally, returns <tt>true</tt> if and only if
     * this map contains a mapping for a key <tt>k</tt> such that
     * <tt>(key==null ? k==null : key.equals(k))</tt>.  (There can be
     * at most one such mapping.)
     *
     * @param key key whose presence in this map is to be tested
     * @return <tt>true</tt> if this map contains a mapping for the specified
     *         key
     * @throws ClassCastException if the key is of an inappropriate type for
     *         this map
     * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
     * @throws NullPointerException if the specified key is null and this map
     *         does not permit null keys
     * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
     */
    boolean containsKey(Object key);

2.1.1.2 entrySet()方法和内部接口Entry<K,V>    

        Map接口中还定义了一个内部接口Entry<K,V>,用来表示Map中的映射项;另外有一个方法Set<Map.Entry<K, V>> entrySet();两者结合可以用来遍历Map中的元素。比如下面写法:

for (Map.Entry<String, Object> entry : map.entrySet()) {
            System.out.println("key:value = " + entry.getKey() + ":" + entry.getValue());
        }

2.1.1.3  V put(K key, V value)方法

       将映射关系放入Map结构中,需要注意的是如果此映射以前包含一个该键的映射关系,则用指定值替换旧值(当且仅当m.containsKey(k)返回 true 时,才能说映射 m 包含键 k 的映射关系)。

       返回值:返回之前与 key 关联的旧值,如果没有针对 key 的映射关系,则返回 null。(如果该实现支持 null 值,则返回 null 也可能表示此映射以前将 null 与 key 关联)。

2.1.1.4  Set<K> keySet() 和 Collection<V> values()

        Set<K> keySet()方法返回Map中所有映射关系中key的集合,因为key是不能重复的,所以返回的是Set结构;Collection<V> value()方法返回Map中所有映射关系值value的集合,value的值是可以重复的,所以返回的是Collection结构,重复的value值也会重复返回,不会去重。

       通过keySet()方法和get(Object key)方法也能遍历Map:

for (String key : map.keySet()) {
            System.out.println("map.get(" + key + ") = " + map.get(key));
        }

2.1.1.5  equals(Object o) 和 hashCode()

        equals(Object o)方法用来比较两个Map是否相等,如果给定的对象也是一个映射,并且这两个映射表示相同的映射关系,则返回 true。更确切地讲,如果 m1.entrySet().equals(m2.entrySet()),则两个映射 m1 和 m2 表示相同的映射关系。

       hashCode()方法返回此映射的哈希码值。映射的哈希码定义为此映射 entrySet() 中每个项的哈希码之和。这确保 m1.equals(m2) 对于任意两个映射 m1 和 m2 而言,都意味着 m1.hashCode()==m2.hashCode(),正如Object.hashCode()中的要求。

       equals(Object o)和hashCode()的具体实现,可以参考java.util.AbstractMap中的实现,后面也会介绍。

2.1.1.6  JDK1.8版本新增加的特性

1、default V getOrDefault(Object key, V defaultValue)

        一个default方法,返回Map中key对应的value值,如果没有这个key,返回传入的默认值defaultValue:

    default V getOrDefault(Object key, V defaultValue) {
        V v;
        return (((v = get(key)) != null) || containsKey(key))
            ? v
            : defaultValue;
    }

使用示例:

 public static void main(String[] args) {
       Map map = new HashMap();
       map.put("1", 12);
       map.put("11", 12);
       map.put("12", 13);
       System.out.println(map.getOrDefault("11",120));
       System.out.println(map.getOrDefault("13",120));
    }

输出结果:

12
120

2、default void forEach(BiConsumer<? super K, ? super V> action) 

      该forEach方法采用函数式编程,传入一个函数式接口BiConsumer,用来迭代遍历Map,如下:

 map.forEach((k, v) -> System.out.println("key:value = " + k + ":" + v));

3、default void repalceAll(BiFunction<? super K, ? super V> action) 

default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
        Objects.requireNonNull(function);
        for (Map.Entry<K, V> entry : entrySet()) {
            K k;
            V v;
            try {
                k = entry.getKey();
                v = entry.getValue();
            } catch(IllegalStateException ise) {
                // this usually means the entry is no longer in the map.
                throw new ConcurrentModificationException(ise);
            }

            // ise thrown from function is not a cme.
            v = function.apply(k, v);

            try {
                entry.setValue(v);
            } catch(IllegalStateException ise) {
                // this usually means the entry is no longer in the map.
                throw new ConcurrentModificationException(ise);
            }
        }
    }

      用给定的函数action对Map中的每个映射进行处理,并用每个映射执行action返回的结果替换映射对应的value值。举个例子:

public static void main(String[] args) {
        Map<String,Integer> map = new HashMap();
        map.put("1", 1);
        map.put("2", 2);
        map.put("3", 3);
        System.out.println(map);
        //对map中的每个映射value值都加1
        map.replaceAll((k,v) ->  v +=1);
        System.out.println(map);
    }

输出结果:

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

4、default V putIfAbsent(K key, V value)

default V putIfAbsent(K key, V value) {
        V v = get(key);
        if (v == null) {
            v = put(key, value);
        }

        return v;
    }

     如果入参中的key在Map中没有映射的value值或者映射的value值为null,把入参中的value值与之关联,返回null。反之,返回Map中该key映射的value值。

使用示例:

public static void main(String[] args) {
        Map<String, Integer> map = new HashMap();
        map.put("1", 1);
        map.put("2", null);
        System.out.println("初始map:" + map);
        System.out.println("map.putIfAbsent(\"1\", 11)返回值:" + map.putIfAbsent("1", 11));
        System.out.println("map.putIfAbsent(\"2\", 2)返回值:" + map.putIfAbsent("2", 2));
        System.out.println("map.putIfAbsent(\"3\", 3)返回值:" + map.putIfAbsent("3", 3));
        System.out.println("处理后map:" + map);
    }

输出结果:

初始map:{1=1, 2=null}
map.putIfAbsent("1", 11)返回值:1
map.putIfAbsent("2", 2)返回值:null
map.putIfAbsent("3", 3)返回值:null
处理后map:{1=1, 2=2, 3=3}

       注:还有一些其它的default函数,这里不再一一列举了,都是JDK1.8中根据default特性新加在Map接口中的,大家可以自己了解下,实际开发使用会便利一些。

       以上,Map接口暂时就写这么多~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值