Java Map接口总结 从虚方法(子类需要实现的方法) 默认方法 静态方法层面去理解

Map接口源码重点

  1. Map接口有子接口Entry,对应着其子类Map有内部类Entry,Entry是键值对,一般的Map是由键值对Entry数组即Entry[]组成的
  2. Map接口有很多需要子类实现的方法,用这些子类实现的方法,Map也实现了许多默认方法

Map接口实现

子类需要实现的方法

方法名作用
boolean isEmpty()如果该Map映射没有Entry键值对,即为空返回true
int size()返回此Map映射中的Entry键值对个数
boolean containsKey(Object key)如果此Map中包含指定键key的映射,则返回true,键是唯一的
boolean containsValue(Object value)如果此Map将一个或多个键映射到指定值value,则返回true
V get(Object key)返回指定键key映射到的值,如果此Map不包含该键的映射,则返回null
V put(K key, V value)将指定值value与此映射中的指定键key关联,如果映射以前包含键的映射,则旧值将替换为指定值,如果以前不存在则在Map中创建新映射
V remove(Object key)从该Map中删除包含指定键key的映射
void putAll(Map map)将指定Map的所有映射复制到此Map
void clear()从此Map中删除所有映射
Set keySet()返回此Map中包含所有键的Set视图
Collection values()返回此Map中包含所有值的Collection视图
Set<Map.Entry> entrySet()返回此Map中包含所有键值对Entry的Set视图
boolean equals(Object o)将指定的对象与此Map进行相等性比较,比较方式是m1.entrySet().equals(m2.entrySet()),即比较entrySet方法返回的键值对集合
int hashCode()返回此Map的哈希值

默认方法(default)

getOrDefault方法

  1. 获取key对应的值,如果使用get方法取出来的值不为null直接返回该值
  2. 如果取出来的值为null,则调用containsKey方法判断该key在不在Map的键值对里面
  3. 如果在里面说明,取出来的null就是键值对的值,如果不在里面说明Map中没有以该key为键的键值对,则返回默认值 defaultValue
default V getOrDefault(Object key, V defaultValue) {
   V v;
    return (((v = get(key)) != null) || containsKey(key))
        ? v
        : defaultValue;
}

putIfAbsent方法

  1. 使用get(key)获取该键值对对应的值v,如果获取的值为null,则说明有两种情况
  2. 第一种是该Map中有该键值对Entry,但其值确实为null,第二种是Map中无该键key对应的键值对
  3. 第一种是将其值更新为给定的value,第二种新添加一个键值对Entry,其键为key,值为value
  4. 如果第一步获取的值不为null,直接返回,如果第一步为null,返回的是put返回的值,put返回的值是老值也为null
  5. 函数的作用可以理解为如果键值对不存在则添加该键值对
default V putIfAbsent(K key, V value) {
    V v = get(key);
    if (v == null) {
        v = put(key, value);
    }

    return v;
}

remove方法

  1. 使用get(key)获取该键key对应的键值对的值curValue
  2. 如果使用Objects.equals判断找到的值curValue和给定的值value不相等,或者如果curValue为空且该key对应的键值对根本就不在该Map里面,则直接返回false,即没找到该键值对则返回false
  3. 如果找到了该键值对,且键值对键和值都和给定的key和value相等,才执行remove操作,从Map中删除该键值对Entry,并返回true
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;
}

boolean replace方法

  1. 和上面一样先进行get查找操作,如果没找到该键值对,或找到的不是自己想要的,即键值对的值和预期值不一样,直接返回false
  2. 如果找到了该键值对,调用put方法,将该键值对Entry的值设为新值newValue,并返回ture
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;
}

V replace方法

思路和上面一样,先找到key对应的键值对,如果找到了才设置该键值对的值为新值value,没找到直接返回null,不用和上面一样比较键值对的值是否和预期值一样

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

forEach方法

  1. 调用Objects.requireNonNull方法,如果action为空则抛出异常
  2. action的类是BiConsumer,BiConsumer和Consumer都是函数式接口,只不过BiConsumer的void accept(T t, U u)表示接收两个参数且无返回值的操作,而Consumer的void accept(T t)表示接收一个参数且无返回值的操作,为什么这里用到了BiConsumer呢,因为Map的键值对刚好就有两个属性,键key和值value
  3. for循环遍历entrySet方法返回的键值对Entry集合,取出其中每个键值对Entry,然后取出它们的键k,值v,最后放入action的accept中进行一个接收两个参数且无返回值的操作
  4. 取出entry中的键和值为什么会出现异常呢,因为同时有线程并发的修改此Map集合的该键值对Entry,实现方式有一个修改次数属性,每次一个线程进行操作之前记录当前修改次数的值,最后修改的时候判断当前修改次数的值是否和之前记录下来的不同,不同则直接抛出异常
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) {
            throw new ConcurrentModificationException(ise);
        }
        action.accept(k, v);
    }
}

replaceAll方法

  1. 和上面类似先判断是否为空,再遍历,而BiFunction和Function函数式接口的差别和上面也一样,BiFunction接口的apply方法表示一个有两个参数且有返回值的操作,而Function接口的apply方法表示一个有一个参数且有返回值的操作
  2. for循环遍历Entry集合取出键k和值v,然后把k和v传入function.apply进行操作,返回一个新的v,使用entry.setValue将改entry的值设为新的值,Map的一般实现就是由Entry数组或Entry链表实现的,所以改Entry的值就可以改变Map中的映射值
  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) {
                throw new ConcurrentModificationException(ise);
            }
            v = function.apply(k, v);
            try {
                entry.setValue(v);
            } catch (IllegalStateException ise) {
                throw new ConcurrentModificationException(ise);
            }
        }
    }

compute方法

  1. 和上面replaceAll类似,只不过replaceAll是对所有键值对进行操作,而compute只针对其中一个键值对
  2. 使用Objects.requireNonNull如果判断出remappingFunction为null,直接抛出异常
  3. 使用get(key)获取对应键值对的值oldValue,将key和oldValue放入remappingFunction的apply进行一个接收两个参数且有返回值的操作,返回新的值
  4. 如果返回的新值为null,则判断原先键值对是否存在,先判断原来的值oldValue是否为 null,不为null则原来键值对存在,如果为null,调用containsKey判断键值对存不存在,如果存在,则移除该键值对并返回null,如果键值对一开始就不存在则直接返回null
  5. 如果返回的新值不为空,则可能有两个操作,原先键值对不存在则调用put添加新键值对,原先键值对存在则更新原先键值对的值,不管怎样都会返回新的值
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) { 
        if (oldValue != null || containsKey(key)) {
            remove(key);
            return null;
        } else {
            return null;
        }
    } else {
        put(key, newValue);
        return newValue;
    }
}

computeIfAbsent方法

和上面compute类似,只是当key对应的键值对不存在或其值为null,则调用Function.apply方法进行一个接收一个参数值且有返回值的操作,根据key计算出新的值,如果新值不为null,则调用put方法添加该键值对或修改该键值对

 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;
}

computeIfPresent方法

和computeIfAbsent相反,只有当key对应的键值对存在且其值不为null,才调用 BiFunction.apply方法进行一个接收两个参数值且有返回值的操作,根据key和oldValue计算出新值,如果新值不为null则调用put更新键值对并返回新值,如果为null则删除该键值对并返回null

 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);
        if (newValue != null) {
            put(key, newValue);
            return newValue;
        } else {
            remove(key);
            return null;
        }
    } else {
        return null;
    }
}

merge方法

  1. 先判断空,如果为空抛出异常
  2. 根据get取出值,如果值为空,直接将newValue设为给定的value,如果不为空调用 BiFunction.apply对oldValue和value进行一个有返回值的操作,将返回值设为新值newValue
  3. 如果新值为null,则删除该键值对,否则更新该键值对的值
 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);
    if (newValue == null) {
        remove(key);
    } else {
        put(key, newValue);
    }
    return newValue;
}

静态方法(static)

of方法

调用ImmutableCollections获取一个内部没有键值对的且不可改变的Map

static <K, V> Map<K, V> of() {
    return ImmutableCollections.Map0.instance();
}

of(K k1, V v1)方法

调用ImmutableCollections获取一个内部有一个键值对的且不可改变的Map

static <K, V> Map<K, V> of(K k1, V v1) {
  return new ImmutableCollections.Map1<>(k1, v1);
}

以此类推最多一个of可以构造一个一个内部有十个键值对的且不可改变的Map

ofEntries方法

根据给定Entry数组entries,创建对应的不可改变的Map

static <K, V> Map<K, V> ofEntries(Entry<? extends K, ? extends V>... entries) {
    if (entries.length == 0) { 
        return ImmutableCollections.Map0.instance();
    } else if (entries.length == 1) {
        return new ImmutableCollections.Map1<>(entries[0].getKey(),
                                               entries[0].getValue());
    } else {
        Object[] kva = new Object[entries.length << 1];
        int a = 0;
        for (Entry<? extends K, ? extends V> entry : entries) {
            kva[a++] = entry.getKey();
            kva[a++] = entry.getValue();
        }
        return new ImmutableCollections.MapN<>(kva);
    }
}

entry方法

返回包含给定键k和值v的不可变的键值对Entry

static <K, V> Entry<K, V> entry(K k, V v) {
    return new KeyValueHolder<>(k, v);
}

Entry子接口实现

子类需要实现的方法

方法名作用
K getKey()返回此键值对的键
V getValue()返回此键值对的值
V setValue(V value)设置此键值对的值
boolean equals(Object o)将指定的键值对与该键值对进行相等性比较,只有键和值同时相等才相等
int hashCode()返回此键值对的哈希值

静态方法

comparingByKey方法

直接调用键值对Entry的键进行compareTo比较,需要键值对Entry的键实现Comparable接口,也就是可比较的意思,默认的a.compareTo(b),类似a - b

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

comparingByValue方法

直接调用键值对Entry的值进行compareTo比较,需要键值对Entry的值实现Comparable接口,也就是可比较的意思,默认的a.compareTo(b),类似a - b

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());
}

comparingByKey(Comparator cmp)方法

使用比较器Comparator让键值对Entry的键进行比较,默认的c.compare(a, b),类似a - b

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());
}

comparingByValue(Comparator cmp)方法

使用比较器Comparator让键值对Entry的值进行比较,默认的c.compare(a, b),类似a - b

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());
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lolxxs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值