Map接口
Map集合是用键值对作为存储元素的集合,所以它可以通过key值来操作集合中的键值对元素。
public interface Map<K,V> {}
可以注意到Map接口是一个顶层接口,与Collection没有任何联系,甚至它都没有继承Iterable接口,说明Map是不可迭代的。但是Map集合提供了方法将集合中的元素转换成一个可迭代接口实例。
Map常用方法
1、添加元素
// 向集合中添加 键值对,但是如果key值存在,就替换对应的value值
V put(K key, V value);
// 将集合m中的键值对全部存放到本集合中
void putAll(Map<? extends K, ? extends V> m);
2、删除元素
// 根据键key移除Map集合中对应的键值对, 并返回value值
V remove(Object key);
// 清除集合中所有的键值对
void clear();
3、替换元素
// 向集合中添加 键值对,但是如果key值存在,就替换对应的value值
V put(K key, V value);
4、其他方法
// Map集合键值对中是否包括这个键key
boolean containsKey(Object key);
// Map集合键值对中是否包括这个value值
boolean containsValue(Object value);
// 返回Map集合中所有的键组成的Set集合。因为键是唯一的,所以是Set集合
Set<K> keySet();
// 返回Map集合中所有的值组成的Collection集合。因为值是可以重复的。
Collection<V> values();
// 返回Map集合中所有的键值对组成的Set集合。因为键值对也是唯一的。
Set<Map.Entry<K, V>> entrySet();
// 通过键key获取对应的值
V get(Object key);
// Map集合元素数量
int size();
// 集合是否为空
boolean isEmpty();
Map内部接口Entry
因为Map集合是用键值对储存元素的,所以它提供了键值对类的顶层接口Entry。它提供了三个重要方法:
- K getKey(); 获取key值
- V getValue(); 获取value值
- V setValue(V value); 替换value值
Entry 提供了equals和hashCode方法,强制子类实现这个两个方法:
// 比较两个Entry是否相等的方法
boolean equals(Object o);
// 因为复写了equals方法,必须复写hashCode方法。来保证相同的Entry 它们的hashCode值必须也是相同的
int hashCode();
它还提供四个静态方法,返回一个Comparator<Map.Entry<K,V>>比较器,用来比较两个Entry的大小的。
// 工具类方法, 返回一个根据key值大小的比较器Comparator。
// 调用Comparator的compare(T o1, T o2)方法,就可以比较两个Map.Entry大小,是根据Map.Entry的key值来判断的。
// 所以必须保证key值的类型是实现Comparable接口,来确保key值是可以比较的
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());
}
// 工具类方法,返回一个根据value值大小的比较器Comparator。
// 所以必须保证value值的类型是实现Comparable接口,来确保value值是可以比较的
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());
}
// 工具类方法,根据一个key值比较器Comparator<? super K> 生成一个 键值对比较器Comparator<Map.Entry<K, V>>
// 很简单,因为键值对比较器Comparator的compare(T o1, T o2)方法 参数类型是Map.Entry,
// 然后调用Map.Entry的getKey方法获取对应key值,然后调用key值比较器Comparator的compare方法就实现比较功能了。
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());
}
// 工具类方法,根据一个value值比较器Comparator<? super V> 生成一个 键值对比较器Comparator<Map.Entry<K, V>>
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());
}
Map接口提供默认实现方法
因为Map集合是用键值对存储的,所以Map接口提供了一些根据key值操作集合的默认实现方法,主要是键值对的删除和替换方法。‘ 再说这些方法之前,我们想一个问题,操作这些键值对,有两种方法,一种是根据key值(也就是key值在集合中存在),另一种是根据键值对(也就是说不仅key值在集合中存在,而且value值也要相等)- 怎么判断key值在集合中存在?
使用containsKey(key)方法返回true就行了。
但是有个问题,一般情况下我们都要得到key所对应的原value值,所以一般都是先调用V get(Object key)方法,然后再去调用containsKey(key)方法。
有人会说这不是傻么,不会先调用containsKey(key)方法,如果存在再调用get(Object key)方法么?这个就涉及到效率问题了,因为这两个方法都是遍历整个集合来查找对应的键值对,所以尽量不要同时使用这个两个方法。
这里就有一个小技巧了,get(Object key)返回值如果不是null,那么说明找到对应的键值对了,所以这个key值是包含的。
但是坑爹的是get(Object key)返回值是null,并不代表没有找到对应键值对,因为Map集合允许value值为null(甚至key值也允许为null)。所以还必须调用containsKey(key)方法进行二次循环遍历查找。
所以这个表示key值存在的判断式(((v = get(key)) != null) || containsKey(key))
- 怎么判断键值对相等?
先通过get(Object key)查找curValue值,然后判断这个curValue值与传入的value是否相等,不相等就直接返回。相等的话,就判断key值在集合中是否存在。 一般判断条件如下:
// 根据key值获取对应curValue值
Object curValue = get(key);
// 1. 如果值不相等,即!Objects.equals(curValue, value)为true,那么就直接返回false
// 2. 如果值相等,判断key值在集合中是否存在。
// (curValue == null && !containsKey(key)) 可以改写成
// !(curValue != null || containsKey(key))
if (!Objects.equals(curValue, value) ||
(curValue == null && !containsKey(key))) {
return false;
}
package java.util;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.io.Serializable;
public interface Map<K,V> {
// 添加元素
// 向集合中添加 键值对,但是如果key值存在,就替换对应的value值
V put(K key, V value);
// 将集合m中的键值对全部存放到本集合中
void putAll(Map<? extends K, ? extends V> m);
// 删除元素
// 根据键key移除Map集合中对应的键值对, 并返回value值
V remove(Object key);
// 移除Map集合中键值与key相等 v值与value相等的键值对(而不是只要求key相等)
default boolean remove(Object key, Object value) {
// 根据key值获取对应curValue值
Object curValue = get(key);
// 1. 如果值不相等,即!Objects.equals(curValue, value)为true,那么就直接返回false
// 2. 如果值相等,判断key值在集合中是否存在。
// (curValue == null && !containsKey(key)) 可以改写成
// !(curValue != null || containsKey(key))
if (!Objects.equals(curValue, value) ||
(curValue == null && !containsKey(key))) {
return false;
}
remove(key);
return true;
}
// 清除集合中所有的键值对
void clear();
// 更新元素
// 遍历集合中所有键值对元素, 通过BiFunction的apply方法,获取一个新的value值,然后替换键值对中的原来的value值
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);
}
// 通过BiFunction的apply方法,获取一个新的value值,然后替换键值对中的原来的value值
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);
}
}
}
// 如果key值对应的v值为null,就将这个键值对中的v替换成新的value值。
// 如果key值对应的v值不为null,就不做替换操作
default V putIfAbsent(K key, V value) {
V v = get(key);
if (v == null) {
v = put(key, value);
}
return v;
}
// 找到与key、oldValue相等的键值对,然后将这个键值对中的value值替换成新newValue
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;
}
// 找到与key值相等的键值对,然后将这个键值对中的curValue值替换成新value值,并返回原来的curValue值
default V replace(K key, V value) {
V curValue;
if (((curValue = get(key)) != null) || containsKey(key)) {
curValue = put(key, value);
}
return curValue;
}
// 如果Map集合不包含这个key值,或者key值对应的value为null,那么就调用mappingFunction的apply方法,
// 生成新的newValue值。将这个newValue值和key值存放到Map集合中。
default V computeIfAbsent(K key,
Function<? super K, ? extends V> mappingFunction) {
Objects.requireNonNull(mappingFunction);
V v;
if ((v = get(key)) == null) {
V newValue;
// 注意 这里得到的新值不为null的时候,才添加到集合中
if ((newValue = mappingFunction.apply(key)) != null) {
put(key, newValue);
return newValue;
}
}
return v;
}
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;
}
}
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;
}
}
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;
}
// 查询方法
// Map集合键值对中是否包括这个键key
boolean containsKey(Object key);
// Map集合键值对中是否包括这个value值
boolean containsValue(Object value);
// 返回Map集合中所有的键组成的Set集合。因为键是唯一的,所以是Set集合
Set<K> keySet();
// 返回Map集合中所有的值组成的Collection集合。因为值是可以重复的。
Collection<V> values();
// 返回Map集合中所有的键值对组成的Set集合。因为键值对也是唯一的。
Set<Map.Entry<K, V>> entrySet();
// 通过键key获取对应的值
V get(Object key);
// 根据key值获取Map集合中对应value值,如果没有获取到,就返回默认值defaultValue
default V getOrDefault(Object key, V defaultValue) {
V v;
// 这里还要调用containsKey方法,这是因为Map集合允许存放value值为空的键值对
// get(key)返回null,并不代表没有这个键值对(甚至key值都可以为null)。所以还要调用containsKey方法。
return (((v = get(key)) != null) || containsKey(key))
? v
: defaultValue;
}
// Map集合元素数量
int size();
// 集合是否为空
boolean isEmpty();
// 比较两个Map集合是否相等的方法
boolean equals(Object o);
// 因为复写了equals方法,必须复写hashCode方法。来保证相同的Map集合 它们的hashCode值必须也是相同的
int hashCode();
// 遍历集合中所有键值对元素,使它们都调用action的accept方法
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);
}
}
// 表示键值对实体的顶层接口。
// 它提供了获取键值getKey方法,获取value值的getValue方法,已经替换value值的setValue方法。
interface Entry<K,V> {
// 获取key值
K getKey();
// 获取value值
V getValue();
// 替换value值
V setValue(V value);
// 比较两个Entry是否相等的方法
boolean equals(Object o);
// 因为复写了equals方法,必须复写hashCode方法。来保证相同的Entry 它们的hashCode值必须也是相同的
int hashCode();
// 工具类方法, 返回一个根据key值大小的比较器Comparator。
// 调用Comparator的compare(T o1, T o2)方法,就可以比较两个Map.Entry大小,是根据Map.Entry的key值来判断的。
// 所以必须保证key值的类型是实现Comparable接口,来确保key值是可以比较的
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());
}
// 工具类方法,返回一个根据value值大小的比较器Comparator。
// 所以必须保证value值的类型是实现Comparable接口,来确保value值是可以比较的
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());
}
// 工具类方法,根据一个key值比较器Comparator<? super K> 生成一个 键值对比较器Comparator<Map.Entry<K, V>>
// 很简单,因为键值对比较器Comparator的compare(T o1, T o2)方法 参数类型是Map.Entry,
// 然后调用Map.Entry的getKey方法获取对应key值,然后调用key值比较器Comparator的compare方法就实现比较功能了。
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());
}
// 工具类方法,根据一个value值比较器Comparator<? super V> 生成一个 键值对比较器Comparator<Map.Entry<K, V>>
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());
}
}
}
AbstractMap抽象类
AbstractMap类是Map接口的子类,实现了Map接口大部分方法,它的子类只需要实现Set<Entry<K,V>> entrySet() 返回一个键值对的集合,就可以使用Map集合的功能了。
添加方法
public V put(K key, V value) {
throw new UnsupportedOperationException();
}
直接抛出异常,如果子类不复写这个方法,那么它是一个不可修改的Map集合。
public void putAll(Map<? extends K, ? extends V> m) {
for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
put(e.getKey(), e.getValue());
}
遍历Map集合m,通过它的entrySet()方法得到可迭代的Set集合,然后将每个键值对存放到本Map集合中。
删除方法
public V remove(Object key) {
// 得到键值对的迭代器
Iterator<Entry<K,V>> i = entrySet().iterator();
Entry<K,V> correctEntry = null;
// 根据key是否为null,分成两部分,虽然这两部分代码逻辑几乎一样
// 这样做主要是减少判断,因为合成一部分的话,判断条件就要增加
if (key==null) {
while (correctEntry==null && i.hasNext()) {
Entry<K,V> e = i.next();
if (e.getKey()==null)
correctEntry = e;
}
} else {
while (correctEntry==null && i.hasNext()) {
Entry<K,V> e = i.next();
if (key.equals(e.getKey()))
correctEntry = e;
}
}
V oldValue = null;
// 如果找这个键值对correctEntry,删除它,并返回它的value值
if (correctEntry !=null) {
oldValue = correctEntry.getValue();
i.remove();
}
return oldValue;
}
通过entrySet().iterator()方法,得到键值对的迭代器,然后遍历键值对,找到与key值相等的键值对,删除它,并返回对应的value值,如果没找到,就返回null。
public void clear() {
entrySet().clear();
}
调用entrySet集合的clear()方法。
查找元素
public boolean containsKey(Object key) {
// 得到键值对的迭代器
Iterator<Map.Entry<K,V>> i = entrySet().iterator();
// 根据key是否为null,分成两部分
if (key==null) {
while (i.hasNext()) {
Entry<K,V> e = i.next();
if (e.getKey()==null)
return true;
}
} else {
while (i.hasNext()) {
Entry<K,V> e = i.next();
if (key.equals(e.getKey()))
return true;
}
}
return false;
}
通过entrySet().iterator()方法,得到键值对的迭代器,然后遍历键值对,找到与key值相等的键值对,返回true,否则返回false。
public boolean containsValue(Object value) {
// 得到键值对的迭代器
Iterator<Entry<K,V>> i = entrySet().iterator();
// 根据value是否为null,分成两部分,
if (value==null) {
while (i.hasNext()) {
Entry<K,V> e = i.next();
if (e.getValue()==null)
return true;
}
} else {
while (i.hasNext()) {
Entry<K,V> e = i.next();
if (value.equals(e.getValue()))
return true;
}
}
return false;
}
通过entrySet().iterator()方法,得到键值对的迭代器,然后遍历键值对,找到与value值相等的键值对,返回true,否则返回false。
public V get(Object key) {
// 得到键值对的迭代器
Iterator<Entry<K,V>> i = entrySet().iterator();
// 根据key是否为null,分成两部分,
if (key==null) {
while (i.hasNext()) {
Entry<K,V> e = i.next();
if (e.getKey()==null)
return e.getValue();
}
} else {
while (i.hasNext()) {
Entry<K,V> e = i.next();
if (key.equals(e.getKey()))
return e.getValue();
}
}
return null;
}
通过entrySet().iterator()方法,得到键值对的迭代器,然后遍历键值对,找到与key值相等的键值对,并返回对应的value值,如果没找到,就返回null。
遍历Map集合
我们知道Map接口可以返回三个集合,分别是key值组成的Set<K>集合,value值组成的Collection<V>集合,以及键值对组成的Set<Map.Entry<K, V>>集合。
其实这里面最重要的是键值对组成的Set集合,因为其他两个集合可以通过这个集合得到。
public Set<K> keySet() {
// 使用keySet变量做缓存用的,这样只有第一次时需要遍历entrySet()集合。
Set<K> ks = keySet;
if (ks == null) {
// 创建一个Set集合,对它的操作都调用Map集合对应方法。
ks = new AbstractSet<K>() {
public Iterator<K> iterator() {
// 创建一个迭代器,利用 entrySet().iterator()的迭代器来实现本Iterator实例的方法。
return new Iterator<K>() {
private Iterator<Entry<K,V>> i = entrySet().iterator();
public boolean hasNext() {
return i.hasNext();
}
public K next() {
return i.next().getKey();
}
public void remove() {
i.remove();
}
};
}
public int size() {
return AbstractMap.this.size();
}
public boolean isEmpty() {
return AbstractMap.this.isEmpty();
}
public void clear() {
AbstractMap.this.clear();
}
public boolean contains(Object k) {
return AbstractMap.this.containsKey(k);
}
};
keySet = ks;
}
return ks;
}
public Collection<V> values() {
// 使用values变量做缓存用的,防止每次都遍历创建
Collection<V> vals = values;
if (vals == null) {
// 创建一个Collection集合,对它的操作都调用Map集合对应方法。
vals = new AbstractCollection<V>() {
public Iterator<V> iterator() {
// 创建一个迭代器,利用 entrySet().iterator()的迭代器来实现本Iterator实例的方法。
return new Iterator<V>() {
private Iterator<Entry<K,V>> i = entrySet().iterator();
public boolean hasNext() {
return i.hasNext();
}
public V next() {
return i.next().getValue();
}
public void remove() {
i.remove();
}
};
}
public int size() {
return AbstractMap.this.size();
}
public boolean isEmpty() {
return AbstractMap.this.isEmpty();
}
public void clear() {
AbstractMap.this.clear();
}
public boolean contains(Object v) {
return AbstractMap.this.containsValue(v);
}
};
values = vals;
}
return vals;
}
AbstractMap的内部类 SimpleEntry
SimpleEntry实现了Map.Entry接口,表示一个简单地键值对类。它有两个成员属性key、value来存储键值对。
public static class SimpleEntry<K,V>
implements Entry<K,V>, java.io.Serializable
{
private static final long serialVersionUID = -8499721149061103585L;
private final K key;
private V value;
// 构造函数时,就传入key值和value值
public SimpleEntry(K key, V value) {
this.key = key;
this.value = value;
}
// 或者使用另一个entry来初始化key值和value值
public SimpleEntry(Entry<? extends K, ? extends V> entry) {
this.key = entry.getKey();
this.value = entry.getValue();
}
public K getKey() {
return key;
}
public V getValue() {
return value;
}
// 替换value值,并返回原来的oldValue值
public V setValue(V value) {
V oldValue = this.value;
this.value = value;
return oldValue;
}
// 当key值与value值都相等时,就说明两个entry值相等
public boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
return eq(key, e.getKey()) && eq(value, e.getValue());
}
// 保证两个相等的entry,它们的hashCode值必须也相同
public int hashCode() {
return (key == null ? 0 : key.hashCode()) ^
(value == null ? 0 : value.hashCode());
}
public String toString() {
return key + "=" + value;
}
}
AbstractMap的内部类 SimpleImmutableEntry
SimpleImmutableEntry表示一个不可修改键值对Entry类,也就是说它的setValue(V value)方法会直接抛出异常,其他方法与SimpleEntry类一样。
public static class SimpleImmutableEntry<K,V>
implements Entry<K,V>, java.io.Serializable
{
private static final long serialVersionUID = 7138329143949025153L;
private final K key;
private final V value;
// 构造函数时,就传入key值和value值
public SimpleImmutableEntry(K key, V value) {
this.key = key;
this.value = value;
}
// 当key值与value值都相等时,就说明两个entry值相等
public SimpleImmutableEntry(Entry<? extends K, ? extends V> entry) {
this.key = entry.getKey();
this.value = entry.getValue();
}
public K getKey() {
return key;
}
public V getValue() {
return value;
}
// 因为是不可修改键值对Entry类,所以setValue方法直接抛出异常
public V setValue(V value) {
throw new UnsupportedOperationException();
}
// 当key值与value值都相等时,就说明两个entry值相等
public boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
return eq(key, e.getKey()) && eq(value, e.getValue());
}
// 保证两个相等的entry,它们的hashCode值必须也相同
public int hashCode() {
return (key == null ? 0 : key.hashCode()) ^
(value == null ? 0 : value.hashCode());
}
public String toString() {
return key + "=" + value;
}
}