Java源码阅读--Map

Java源码阅读–Map

结构概览

Map接口由内部类Entry和众多方法组成,具体图下
在这里插入图片描述

Entry

Entry

Entry内的默认方法:

  1. comparingByKey
public static <K extends Comparable<? super K>, V> Comparator<Map.Entry<K,V>> comparingByKey() {
            // 这是强制类型转换,转换成可序列化的Comparator<Map.Entry<K, V>>对象。&的意思是且的意思
            return (Comparator<Map.Entry<K, V>> & Serializable)
                (c1, c2) -> c1.getKey().compareTo(c2.getKey());
        }

这个方法看似不知所云,实际上很简单,返回一个比较俩Entry的key值得Comparator。有些不怎么见得语法是阅读得拦路虎,比如 Comparator<Map.Entry<K, V>> & Serializable ,给爷也整懵了,互联网还是个好东西啊,“这是强制类型转换,转换成可序列化的Comparator<Map.Entry<K, V>>对象。&的意思是且的意思”

样例:

        Map<Integer,Integer> map1 = new HashMap<>();
        map1.put(1,1);
        map1.put(2,2);
        map1.put(3,3);
        Map<Integer,Integer> map2 = new HashMap<>();
        map2.put(1,1);
        map2.put(2,2);
        map2.put(3,3);
        // 获取比较俩Entry的key的Comparator
        Comparator<Map.Entry<Integer, Integer>> entryComparator = Map.Entry.comparingByKey();
        // 构造俩Entry的List,为了方便取Entry
        List<Map.Entry> entries1 = new ArrayList<>(map1.entrySet());
        List<Map.Entry> entries2 = new ArrayList<>(map2.entrySet());
        System.out.println(entryComparator.compare(entries1.get(0), entries2.get(0))); // 相当于比较1,1
        System.out.println(entryComparator.compare(entries1.get(0), entries2.get(1))); // 相当于比较1,2
        System.out.println(entryComparator.compare(entries1.get(1), entries2.get(0))); // 相当于比较2,1

输出:

0
-1
1
  1. comparingByValue
public static <K, V extends Comparable<? super V>> Comparator<Map.Entry<K,V>> comparingByValue() {
            return (Comparator<Map.Entry<K, V>> & Serializable)
                (c1, c2) -> c1.getValue().compareTo(c2.getValue());
        }

与上个方法类似,只是比较了value

  1. comparingByKey
public static <K, V> Comparator<Map.Entry<K, V>> comparingByKey(Comparator<? super K> cmp) {
            Objects.requireNonNull(cmp);
            return (Comparator<Map.Entry<K, V>> & Serializable)
                (c1, c2) -> cmp.compare(c1.getKey(), c2.getKey());
        }

比较Entry的key,不过是按照传入的规则进行比较
样例:

        Map<String,Integer> map1 = new HashMap<>();
        map1.put("1",1);
        map1.put("2",2);
        map1.put("3",3);
        Map<String,Integer> map2 = new HashMap<>();
        map2.put("1",1);
        map2.put("2",2);
        map2.put("3",3);

        Comparator<Map.Entry<String, Integer>> entryComparator = Map.Entry.comparingByKey((c1, c2) -> {
            if (c1.equals(c2)) {
                System.out.println(c1 + "与" + c2 + "相等");
            } else {
                System.out.println(c1 + "与" + c2 + "不相等");
                return -1;
            }
            return 0;
        });
        List<Map.Entry> entries1 = new ArrayList<>(map1.entrySet());
        List<Map.Entry> entries2 = new ArrayList<>(map2.entrySet());
        System.out.println(entryComparator.compare(entries1.get(0), entries2.get(0))); // 相当于比较1,1
        System.out.println(entryComparator.compare(entries1.get(0), entries2.get(1))); // 相当于比较1,2
        System.out.println(entryComparator.compare(entries1.get(1), entries2.get(0))); // 相当于比较2,1

输出:

11相等
0
12不相等
-1
21不相等
-1
  1. comparingByValue
public static <K, V> Comparator<Map.Entry<K, V>> comparingByValue(Comparator<? super V> cmp) {
            Objects.requireNonNull(cmp);
            return (Comparator<Map.Entry<K, V>> & Serializable)
                (c1, c2) -> cmp.compare(c1.getValue(), c2.getValue());
        }

换成比较value,没啥好说的

Default方法阅读

作为一个接口,Map的大部分方法需要实现类去实现,但其也有部分方法有着默认实现

首先看一下Map类的“头”:
在这里插入图片描述
有着俩泛型 K 和 V ,记住就行

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

方法很简单,入参 key 和自定义 defaultValue,通过判定 get(key) 是否为null(如果不为null就一定存在此key和对应value,如果为null则不确定,所以需要判定是否存在此key)和 containsKey(key),来返回具体value或传入的默认值。

  1. forEach
default void forEach(BiConsumer<? super K, ? super V> action) {
        Objects.requireNonNull(action);
        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);
            }
            action.accept(k, v);
        }
    }

forEach方法遍历Map内的每个Entry,也就是每个键值对,我们可以传入指定操作,操作每个键值对(从源码上看不支持写入)

样例:

		Map<Integer,Integer> map = new HashMap<>();
        map.put(1,1);
        map.put(2,2);
        map.put(3,3);
        for (Map.Entry entry : map.entrySet()) {
            System.out.println("key: "+entry.getKey()+" "+"value: "+entry.getValue());
        }
        System.out.println();
        // 使用forEach方法,输出k,v键值对
        map.forEach((k,v) -> {
            System.out.println("key: "+(k+1)+" "+"value: "+(v+1));
        });

输出:

key: 1 value: 1
key: 2 value: 2
key: 3 value: 3

key: 2 value: 2
key: 3 value: 3
key: 4 value: 4
  1. replaceAll
    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);
            }
        }
    }

replaceAll方法类似于forEach方法,但是replaceAll方法支持写入,正如方法名所描述的,此方法可以依据传入的特定操作替换所有 k,v 键值对中的value值。

样例:

        Map<Integer,Integer> map = new HashMap<>();
        map.put(1,1);
        map.put(2,2);
        map.put(3,3);
        for (Map.Entry entry : map.entrySet()) {
            System.out.println("key: "+entry.getKey()+" "+"value: "+entry.getValue());
        }
        System.out.println();
        // 使用replaceAll方法替换value为k+v的值
        map.replaceAll((k,v) -> k+v);
        // 使用forEach方法,输出k,v键值对
        map.forEach((k,v) -> {
            System.out.println("key: "+k+" "+"value: "+v);
        });

输出

key: 1 value: 1
key: 2 value: 2
key: 3 value: 3

key: 1 value: 2
key: 2 value: 4
key: 3 value: 6
  1. putIfAbsent
    default V putIfAbsent(K key, V value) {
        V v = get(key);
        if (v == null) {
            v = put(key, value);
        }

        return v;
    }

这个方法就不要太简单了哈,看不懂的出门左拐看Java基础去

  1. remove
default boolean remove(Object key, Object value) {
        Object curValue = get(key);
        if (!Objects.equals(curValue, value) ||
            (curValue == null && !containsKey(key))) {
            return false;
        }
        remove(key);
        return true;
    }

注意一下其中的 if 语句判断的顺序就好了,按照自然思维,应该是先判断是否存在key,然后判断value值是否相等。
这里的做法恰恰相反,原因是 get(key) 如果不存在 key 则会返回null,然后拿去和传入的value做对比,假设正常人传入正常值,那么只需要一次判断。讲实话,我都不认为需要第二个判断。。。

  1. replace
    default boolean replace(K key, V oldValue, V newValue) {
        Object curValue = get(key);
        if (!Objects.equals(curValue, oldValue) ||
            (curValue == null && !containsKey(key))) {
            return false;
        }
        put(key, newValue);
        return true;
    }

与remove类似,不予赘述

  1. replace
default V replace(K key, V value) {
        V curValue;
        if (((curValue = get(key)) != null) || containsKey(key)) {
            curValue = put(key, value);
        }
        return curValue;
    }

key 存在则替换

  1. computeIfAbsent
    default V computeIfAbsent(K key,
            Function<? super K, ? extends V> mappingFunction) {
        Objects.requireNonNull(mappingFunction);
        V v;
        if ((v = get(key)) == null) {
            V newValue;
            if ((newValue = mappingFunction.apply(key)) != null) {
                put(key, newValue);
                return newValue;
            }
        }

        return v;
    }

如果不存在key,则通过传入的特定操作,得到value值,并将此key.,value添加进map中

样例:

        Map<Integer,Integer> map = new HashMap<>();
        map.put(1,1);
        map.put(2,2);
        map.put(3,3);
        // 使用forEach方法,输出k,v键值对
        map.forEach((k,v) -> {
            System.out.println("key: "+k+" "+"value: "+v);
        });
        System.out.println();
        
        map.computeIfAbsent(4, k -> k+1 );  // 如果key=4不存在,则添加(4,5)键值对

        // 使用forEach方法,输出k,v键值对
        map.forEach((k,v) -> {
            System.out.println("key: "+k+" "+"value: "+v);
        });

输出

key: 1 value: 1
key: 2 value: 2
key: 3 value: 3

key: 1 value: 1
key: 2 value: 2
key: 3 value: 3
key: 4 value: 5
  1. computeIfPresent
    default V computeIfPresent(K key,
            BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
        Objects.requireNonNull(remappingFunction);
        V oldValue;
        if ((oldValue = get(key)) != null) {
            V newValue = remappingFunction.apply(key, oldValue); // calculate the newValue from the key and oldValue
            if (newValue != null) {
                put(key, newValue);
                return newValue;
            } else {
                remove(key); // if newValue is null,remove the key
                return null;
            }
        } else {
            return null;
        }
    }

如果key对应的value不为null,那么就通过key值和旧的value值按照传入的计算方法算出新的value;如果新的value不为null那么就put新的key,value键值对并返回新的value,否则删除key,返回null

样例:

        Map<Integer,Integer> map = new HashMap<>();
        map.put(1,1);
        map.put(2,2);
        map.put(3,3);
        // 使用forEach方法,输出k,v键值对
        printMap(map);
        System.out.println("======");

        // 如果存在key,则保存新的value=key+odlValue=6
        System.out.println(map.computeIfPresent(3, Integer::sum));
        System.out.println("======");

        // 使用forEach方法,输出k,v键值对
        printMap(map);
        System.out.println("======");

        // 返回null,并删除key=3
        System.out.println(map.computeIfPresent(3, (k,v) -> null));
        System.out.println("======");

        // 使用forEach方法,输出k,v键值对
        printMap(map);

输出

key: 1 value: 1
key: 2 value: 2
key: 3 value: 3
======
6
======
key: 1 value: 1
key: 2 value: 2
key: 3 value: 6
======
null
======
key: 1 value: 1
key: 2 value: 2
  1. compute
    default V compute(K key,
            BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
        Objects.requireNonNull(remappingFunction);
        V oldValue = get(key);

        V newValue = remappingFunction.apply(key, oldValue);
        if (newValue == null) {
            // delete mapping
            if (oldValue != null || containsKey(key)) {
                // something to remove
                remove(key);
                return null;
            } else {
                // nothing to do. Leave things as they were.
                return null;
            }
        } else {
            // add or replace old mapping
            put(key, newValue);
            return newValue;
        }
    }

和compute类似,不予赘述

  1. merge
    default V merge(K key, V value,
            BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
        Objects.requireNonNull(remappingFunction);
        Objects.requireNonNull(value);
        V oldValue = get(key);
        V newValue = (oldValue == null) ? value :
                   remappingFunction.apply(oldValue, value); // get the newValue from the passed in value and oldValue
        if(newValue == null) {
            remove(key);
        } else {
            put(key, newValue);
        }
        return newValue;
    }

和compute方法类似,只是是通过旧的value值和传入的value值得出新的value,如果新的value不为null那么就put新的key,value键值对并返回新的value,否则删除key,返回null

好了,Map接口的默认方法就是这些了。我自己看完后觉得收获满满。希望你也一样。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值