Map接口源码重点
- Map接口有子接口Entry,对应着其子类Map有内部类Entry,Entry是键值对,一般的Map是由键值对Entry数组即Entry[]组成的
- 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方法
- 获取key对应的值,如果使用get方法取出来的值不为null直接返回该值
- 如果取出来的值为null,则调用containsKey方法判断该key在不在Map的键值对里面
- 如果在里面说明,取出来的null就是键值对的值,如果不在里面说明Map中没有以该key为键的键值对,则返回默认值 defaultValue
default V getOrDefault(Object key, V defaultValue) {
V v;
return (((v = get(key)) != null) || containsKey(key))
? v
: defaultValue;
}
putIfAbsent方法
- 使用get(key)获取该键值对对应的值v,如果获取的值为null,则说明有两种情况
- 第一种是该Map中有该键值对Entry,但其值确实为null,第二种是Map中无该键key对应的键值对
- 第一种是将其值更新为给定的value,第二种新添加一个键值对Entry,其键为key,值为value
- 如果第一步获取的值不为null,直接返回,如果第一步为null,返回的是put返回的值,put返回的值是老值也为null
- 函数的作用可以理解为如果键值对不存在则添加该键值对
default V putIfAbsent(K key, V value) {
V v = get(key);
if (v == null) {
v = put(key, value);
}
return v;
}
remove方法
- 使用get(key)获取该键key对应的键值对的值curValue
- 如果使用Objects.equals判断找到的值curValue和给定的值value不相等,或者如果curValue为空且该key对应的键值对根本就不在该Map里面,则直接返回false,即没找到该键值对则返回false
- 如果找到了该键值对,且键值对键和值都和给定的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方法
- 和上面一样先进行get查找操作,如果没找到该键值对,或找到的不是自己想要的,即键值对的值和预期值不一样,直接返回false
- 如果找到了该键值对,调用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方法
- 调用Objects.requireNonNull方法,如果action为空则抛出异常
- action的类是BiConsumer,BiConsumer和Consumer都是函数式接口,只不过BiConsumer的void accept(T t, U u)表示接收两个参数且无返回值的操作,而Consumer的void accept(T t)表示接收一个参数且无返回值的操作,为什么这里用到了BiConsumer呢,因为Map的键值对刚好就有两个属性,键key和值value
- for循环遍历entrySet方法返回的键值对Entry集合,取出其中每个键值对Entry,然后取出它们的键k,值v,最后放入action的accept中进行一个接收两个参数且无返回值的操作
- 取出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方法
- 和上面类似先判断是否为空,再遍历,而BiFunction和Function函数式接口的差别和上面也一样,BiFunction接口的apply方法表示一个有两个参数且有返回值的操作,而Function接口的apply方法表示一个有一个参数且有返回值的操作
- 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方法
- 和上面replaceAll类似,只不过replaceAll是对所有键值对进行操作,而compute只针对其中一个键值对
- 使用Objects.requireNonNull如果判断出remappingFunction为null,直接抛出异常
- 使用get(key)获取对应键值对的值oldValue,将key和oldValue放入remappingFunction的apply进行一个接收两个参数且有返回值的操作,返回新的值
- 如果返回的新值为null,则判断原先键值对是否存在,先判断原来的值oldValue是否为 null,不为null则原来键值对存在,如果为null,调用containsKey判断键值对存不存在,如果存在,则移除该键值对并返回null,如果键值对一开始就不存在则直接返回null
- 如果返回的新值不为空,则可能有两个操作,原先键值对不存在则调用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方法
- 先判断空,如果为空抛出异常
- 根据get取出值,如果值为空,直接将newValue设为给定的value,如果不为空调用 BiFunction.apply对oldValue和value进行一个有返回值的操作,将返回值设为新值newValue
- 如果新值为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());
}