Java的编程过程中经常会和Map打交道,现在我们来一起了解一下Map的底层实现,其中的思想结构对我们平时接口设计和编程也有一定借鉴作用。(以下接口分析都是以jdk1.8源码为参考依据)
1. Map An object that maps keys to values. A map cannot contain duplicate keys;each key can map to at most one value.
Map提供三种访问数据的方式: 键值集、数据集、数据-映射,对应下表中的标记为黄色的三个接口。public interface Map<K, V>
方法名 描述void clear() 从此映射中移除所有映射关系(可选操作)。
boolean containsKey(Object key) 如果此映射包含指定键的映射关系,则返回 true。
boolean containsValue(Object value) 如果此映射将一个或多个键映射到指定值,则返回 true。
Set<Map.Entry<K,V>> entrySet() 返回此映射中包含的映射关系的 Set 视图。
boolean equals(Object o) 比较指定的对象与此映射是否相等。
V get(Object key) 返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null。
int hashCode() 返回此映射的哈希码值。
boolean isEmpty() 如果此映射未包含键-值映射关系,则返回 true。
Set<K> keySet() 返回此映射中包含的键的 Set 视图。
V put(K key, V value) 将指定的值与此映射中的指定键关联(可选操作)。
void putAll(Map<? extends K,? extends V> m) 从指定映射中将所有映射关系复制到此映射中(可选操作)。
V remove(Object key) 如果存在一个键的映射关系,则将其从此映射中移除(可选操作)。
int size() 返回此映射中的键-值映射关系数。
Collection<V> values() 返回此映射中包含的值的 Collection 视图。
在Java8中的Map有增添了一些新的接口不在上述表格之中,这里不一一列举。
这里涉及到一个静态内部接口:Map.Entry<K,V> ,用于存储一个键值对,该接口中设置set和get键值和value值的接口。
所以Map中存储数据都是以这种Entry为数据单元存储的。
2. AbatractMapAbstractMap中增加了两个非常重要的成员变量:
transient Set<K> keySet;
transient Collection<V> values;
通过这两个成员变量,我们已经知道Map是如何存储数据的了:键值存入keySet中,value存入values中。(由于Map需要保证键值的唯一性所以选择Set作为键值的存储结构,而Value则对此没有任何要求所以选择Collection作为存储结构)
AbstractMap实现了Map中的部分接口,都是通过调用接口:Set<Entry<K,V>> entrySet() 实现的,而该接口的具体实现却留给了具体的子类。以下代码列出了equal()方法的具体实现:
public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof Map)) return false; Map<?,?> m = (Map<?,?>) o; 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(); if (value == null) { if (!(m.get(key)==null && m.containsKey(key))) return false; } else { if (!value.equals(m.get(key))) return false; } } } catch (ClassCastException unused) { return false; } catch (NullPointerException unused) { return false; } return true; }