一、概述
AbstractMap类是Map大家族中的骨架实现类,在接口与实现类之间构建了一层抽象,其目的是为了复用一些比较通用的函数以及方便扩展,为子类提供共用的方法模板。
二、源码分析
(1) 类的申明,源码如下:
public abstract class AbstractMap<K,V> implements Map<K,V>
AbstractMap类是一个抽象类,并且实现了Map。
(2) 成员变量,源码如下:
//key的集合
transient Set<K> keySet;
//value的集合
transient Collection<V> values;
//元素的集合,元素类型是Entry<K,V>,表示一个键值对
public abstract Set<Entry<K,V>> entrySet();
由于Set是不允许存在相同元素的,所以AbstractMap的keySet中的key值是不可以存在相同元素的,但是values是Collection类型的,所以是可以存在相同元素的。那么Set中为什么不允许相同的元素存在呢,在后面的文章中会详细解读Set的源码。
(3) containsValue()方法与containsKey()方法主要是验证是否是否存在指定的value和key,源码如下:
public boolean containsValue(Object value) {
Iterator<Entry<K,V>> i = entrySet().iterator();
//判断非null主要就是为了防止使用equals()报错。
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();
//这里用的是equals()方法,具体类型用自己的equals判断规则,比如String类型
if (value.equals(e.getValue()))
return true;
}
}
return false;
}
public boolean containsKey(Object key) {
Iterator<Map.Entry<K,V>> i = entrySet().iterator();
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();
//这里用的是equals()方法
if (key.equals(e.getKey()))
return true;
}
}
return false;
}
两个方法结构差不多,检验是否包含key或value,检验的方法也是使用迭代器直接循环集合,都是先判断了参数是否为null,这也就说明了AbstractMap类元素的key和value都可以为null。判断参数非null主要就是为了防止使用equals()报错,如果是null则用==来比较对象的内存地址(基础数据类型还是比较值),如果不是null则用equals()方法进行比较,则保证了有对象重写了equals()方法(比如String)后满足条件。
(4) get()方法,源码如下:
public V get(Object key) {
Iterator<Entry<K,V>> i = entrySet().iterator();
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;
}
判断逻辑与containsKey相同,只是结果需要返回查找到的元素。
(5) put()方法和putAll()方法,源码如下:
//由子类实现,父类不提供具体的操作逻辑
public V put(K key, V value) {
throw new UnsupportedOperationException();
}
//循环要加入的map的entry,然后将键值对加入到当前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());
}
AbstractMap不支持put()方法,所以子类需要重写此方法。
(6) remove()方法,源码如下:
public V remove(Object key) {
Iterator<Entry<K,V>> i = entrySet().iterator();
Entry<K,V> correctEntry = null;
//判断逻辑简单清晰,就是区分一下key是否为null来决定下面用==还是equals()
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;
if (correctEntry !=null) {
oldValue = correctEntry.getValue();
//这里可以直接调用remove()是因为上面i在遍历的时候,找到目标元素时,游标已经记录了位置,所以直接调用迭代器的remove()方法就可以移除元素了
i.remove();
}
return oldValue;
}
(7) clear()方法,源码如下:
public void clear() {
//直接调用set的clear()方法
entrySet().clear();
}
(8) keySet()方法和values()方法,源码如下:
public Set<K> keySet() {
//获取keySet进行判断
Set<K> ks = keySet;
//如果为空则重新创建一个
if (ks == null) {
//初始化一个匿名内部类AbstractSet的实例赋值给ks。本质上就相当于编写一个子类继承AbstractCollection,并且实现了hasNext()、next()等方法,然后将entrySet.iterator赋值给当前的Set
ks = new AbstractSet<K>() {
public Iterator<K> 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() {
Collection<V> vals = values;
if (vals == null) {
//与上面一致,只是返回类型不同,此处是AbstractCollection
vals = new AbstractCollection<V>() {
public Iterator<V> 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;
}
keySet()方法和values()方法最需要掌握的,就是匿名内部类的使用。
(9) equals()方法,源码如下:
public boolean equals(Object o) {
//先判断是否本身
if (o == this)
return true;
//判断是否是Map类型,不是就直接返回false
if (!(o instanceof Map))
return false;
//参数对象转换成Map
Map<?,?> m = (Map<?,?>) o;
//比较Map的容量,不一致则返回false
if (m.size() != size())
return false;
//如果上面的条件都满足,则开始比较内容
try {
Iterator<Entry<K,V>> i = entrySet().iterator();
while (i.hasNext()) {
Entry<K,V> e = i.next();
K key = e.getKey();
V value = e.getValue();
//先判断value是否为空
if (value == null) {
//如果value为null,则参数Map m也要含有存在相同key的键值对的value==null,并且m必须含有相同的key
if (!(m.get(key)==null && m.containsKey(key)))
return false;
} else {
//如果value不为null,则直接调用Object的equals()方法进行相同key的Map m里的value
if (!value.equals(m.get(key)))
return false;
}
}
} catch (ClassCastException unused) {
return false;
} catch (NullPointerException unused) {
return false;
}
return true;
}
最核心的逻辑就是在循环比较的时候,是先判断vaule==null,如果不等于就直接用equals()比较value;如果等于的话就需要同时判断key是否存在以及key对应的value是否为null。
(10) hashCode()方法,源码如下:
public int hashCode() {
int h = 0;
Iterator<Entry<K,V>> i = entrySet().iterator();
while (i.hasNext())
h += i.next().hashCode();
return h;
}
AbstractMap类中的hashCode()实际返回的是所有元素的hashCode的累加值。
(11) toString()方法,源码如下:
public String toString() {
Iterator<Entry<K,V>> i = entrySet().iterator();
if (! i.hasNext())
return "{}";
StringBuilder sb = new StringBuilder();
sb.append('{');
for (;;) {
Entry<K,V> e = i.next();
K key = e.getKey();
V value = e.getValue();
sb.append(key == this ? "(this Map)" : key);
sb.append('=');
sb.append(value == this ? "(this Map)" : value);
if (! i.hasNext())
return sb.append('}').toString();
sb.append(',').append(' ');
}
}
toString()方法的循环逻辑,我们可以看到采用的是for (;;),与while(true)在java中javac的编译结果是一致的,但是在c中,编译为汇编代码for ( ; ; )的指令更少,不占用寄存器,而且没有判断、跳转,比while (true)好。
(12) clone()方法,源码如下:
protected Object clone() throws CloneNotSupportedException {
AbstractMap<?,?> result = (AbstractMap<?,?>)super.clone();
result.keySet = null;
result.values = null;
return result;
}
AbstractMap类的clone()方法不复制键和值本身。
(13)AbstractMap类中还包含两个类,分别是SimpleEntry类和SimpleImmutableEntry类,先看一下SimpleEntry类,源码如下:
public static class SimpleEntry<K,V>
implements Entry<K,V>, java.io.Serializable
{
private static final long serialVersionUID = -8499721149061103585L;
//定义final修饰的key,表示不能被更改
private final K key;
private V value;
//参数为key和value的构造函数
public SimpleEntry(K key, V value) {
this.key = key;
this.value = value;
}
//参数为entry的构造函数
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,返回旧value
public V setValue(V value) {
V oldValue = this.value;
this.value = value;
return oldValue;
}
//比较指定 Entry 和本地是否相等。要求顺序,key-value 必须全相等。只要是 Map 的实现类即可,不同实现也可以相等
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());
}
//键的哈希与值的哈希的异或
public int hashCode() {
return (key == null ? 0 : key.hashCode()) ^
(value == null ? 0 : value.hashCode());
}
public String toString() {
return key + "=" + value;
}
}
(14) SimpleImmutableEntry类,源码如下:
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;
public SimpleImmutableEntry(K key, V value) {
this.key = key;
this.value = value;
}
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;
}
//不可以设置值,直接抛出异常
public V setValue(V value) {
throw new UnsupportedOperationException();
}
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());
}
public int hashCode() {
return (key == null ? 0 : key.hashCode()) ^
(value == null ? 0 : value.hashCode());
}
public String toString() {
return key + "=" + value;
}
}
SimpleImmutableEntry类与SimpleEntry类基本相同,但是还是有几点重要的区别:
* SimpleImmutableEntry类,不仅key是final修饰的,value也是用final修饰的。而SimpleEntry类只有key被final修饰。
* 由于SimpleImmutableEntry类的value被final修饰,所以set()方法会直接抛出异常,不让修改value的值。
三、总结
AbstractMap类和AbstractCollection类,AbstractList类作用相似,AbstractMap类是一个基础实现类,实现了 Map 的主要方法,默认不支持修改。 常用的几种 Map, 比如 HashMap, TreeMap, LinkedHashMap 都继承自它。敬请期待《我的jdk源码(十):AbstractSet Set家族的骨架实现类》。
更多精彩内容,敬请扫描下方二维码,关注我的微信公众号【Java觉浅】,获取第一时间更新哦!