Map集合是用来存储<Key, Value>键值对数据的,是平常开发中使用最多的数据结构之一。Map集合相对List集合来讲结构会稍微复杂一些,因此Map系列会分开写。本文主要分析AbstractMap。html
整个AbstractMap类中只有一个抽象方法:java
public abstract Set<Entry<K,V>> entrySet();
复制代码
也就是说全部的子类都必须实现entrySet()
方法。纵观AbstractMap中的成员方法内部实现,基本都依赖于entrySet()
方法,它返回了Map所保存的键值对。api
AbstractMap有个默认抛UnsupportedOperationException
异常的方法:安全
public V put(K key, V value) {
throw new UnsupportedOperationException();
}
复制代码
整个put方法直接影响了:bash
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默认是不支持修改的,子类若是想实现可变的Map,则须要重写put方法。
有put就应该有remove,来看看remove方法:微信
public V remove(Object key) {
// 使用到了entrySet()获取保存的数据
Iterator<Entry<K,V>> i = entrySet().iterator();
Entry<K,V> correctEntry = null;
// 经过迭代器找到要remove的值
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();
i.remove(); // 调用迭代器的remove方法
}
return oldValue;
}
复制代码
Map的remove方法使用到了entrySet()所返回Set的迭代器的remove方法。数据结构
因此若是你想实现本身的Map结构:
1.当要实现一个不可变的Map时,须要继承AbstractMap,而后实现entrySet() 方法,这个方法返回一个保存全部key-value映射的Set。一般这个Set不支持add()和remove() 方法,Set对应的迭代器也不支持remove()方法。
2. 当要实现一个可变的 Map时,须要在上述操做外,重写put()方法,并且entrySet()返回的Set 的迭代器须要实现remove()方法。oracle
AbstractMap只有两个成员变量:源码分析
transient Set<K> keySet; // 不可序列化
transient Collection<V> values; // 不可序列化
复制代码
注意:从jdk1.8开始,这两个变量再也不使用volatile
修饰,由于调用这两个变量的方法不是同步的,增长volatile
也不能保证线程安全。(本文用的是jdk11)this
这里看一下怎么获取keySet:
public Set<K> keySet() {
Set<K> ks = keySet;
if (ks == null) {
// 自定义一个Set而且实现迭代器
ks = new AbstractSet<K>() {
public Iterator<K> iterator() {
return new Iterator<K>() {
// 使用Entry的Set集合的迭代器
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;
}
复制代码
这里写的很巧妙,没用采用遍历Entry的方式,而是实现了一个自定义Set集合。这个集合再重写iterator
方法,直接调用Entry集合的迭代器。values
也作了一样的处理。
AbstractMap有两个内部类SimpleEntry<K,V>
和SimpleImmutableEntry<K,V>
,它们都实现了Entry<K,V>
和Serializable
。
SimpleEntry:表示值可变的键值对。
private final K key; // 不可变
private V value;
复制代码
提供了相应的setValue
方法:
public V setValue(V value) {
V oldValue = this.value;
this.value = value;
return oldValue; // 注意返回的是旧值
}
复制代码
来看看equals
方法:
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());
}
private static boolean eq(Object o1, Object o2) {
// 由于实际中o1极可能是String类型,因此这里使用了equals,而不是==
return o1 == null ? o2 == null : o1.equals(o2);
}
复制代码
SimpleImmutableEntry:表示不可变的键值对。
private final K key; // 不可变
private final V value; // 不可变
复制代码
它的setValue
方法直接抛出异常:
public V setValue(V value) {
throw new UnsupportedOperationException();
}
复制代码
entrySet()
,子类必须实现;