Map
Map 在 JDK 1.2 提供的接口,是独立与 Collection 的顶级接口,但 Map 也是 Java 集合框架体系中的一员。
public interface Map<K,V> {
}
类文档解读
通过 Map 的类文档了解一下 Map 的基本信息:
- Map 是一种基于
key-value
键值对的数据格式,一个键值对代表一个映射。 - Map 要求 key 不允许重复,每个 key 最多只能对应一个 value,当然不同的 key 可以与相同的 value 进行映射。
- Map 是在 JDK 1.2 时被设计用来替代 Dictionary。Dictionary 类是在 JDK 1.0 版本就提供的抽象类,Hashtable 就直接继承了 Dictionary 类。在 Dictionary 的类注释中已经明确表述它已经过时了,新的实现应该实现 Map 接口。
public abstract class Dictionary<K,V> {
}
- Map 接口提供三个集合视图,分别可以把 Map 的内容映射为 key 的 Set 集合 、value 的集合或 key-value 的 Set 集合。映射的顺序为集合视图的迭代器返回元素的顺序。一些 Map 实现如 TreeMap 可以保证映射的顺序是有序的;而另外一些实现如 HashMap 类,则是无序的。
- Map 的有些实现对 key 的类型有限制。尝试插入不合格的 key 或 value 会引发 NullPointerException 或 ClassCastException 异常。尝试查询是否存在不合格的 key 或 value 可能会引发异常,也可能只是返回 false,具体细节由实现选择。
- key 和 value 是否允许为 null,也要看具体的实现:
key | value | super | 线程安全 |
---|---|---|---|
HashMap | 可以为 null | 可以为 null | AbstractMap |
TreeMap | 不可以为 null | 可以为 null | AbstractMap |
ConcurrentHashMap | 不可以为 null | 不可以为 null | AbstractMap |
Hashtable | 不可以为 null | 不可以为 null | Dictionary |
Map API
Map.Entry
Entry 是 Map 的内部接口,代表一个键值对的映射,Map 就是使用 Entry 存储 key 和 value 的,实现 Map 的实现类都必须实现此接口用来数据存储 key-value 映射。
interface Entry<K,V> {
/**
* 获取对应的 key
* @throws 如果对应的 Entry 被删除,实现类可以选择性抛出 IllegalStateException
*/
K getKey();
/**
* 返回对应的 value
* @throws 如果对应的 Entry 被删除,实现类可以选择性抛出 IllegalStateException
*/
V getValue();
/**
* 替换原有的 value
* @throws 如果 put 方法不支持抛出 UnsupportedOperationException
* @throws 当对象的类型和 Map 里元素类型不兼容时抛出 ClassCastException
* @throws 如果 Map 不支持 null 值且 value 是 null 抛出 NullPointerException
* @throws 如果此 value 的属性阻止其存入 Map 抛出 IllegalArgumentException
* @throws 如果对应的 Entry 被删除,实现类可以选择性抛出 IllegalStateException
*/
V setValue(V value);
/**
* 将指定的对象与此 Entry 比较相等性,如果表示两个 Entry 相等需要:
* (e1.getKey()==null ?
* e2.getKey()==null : e1.getKey().equals(e2.getKey())) &&
* (e1.getValue()==null ?
* e2.getValue()==null : e1.getValue().equals(e2.getValue()))
*/
boolean equals(Object o);
/**
* 返回此 Entry 的 hashCode:
* (e.getKey()==null ? 0 : e.getKey().hashCode()) ^
* (e.getValue()==null ? 0 : e.getValue().hashCode())
* 确保 e1.equals(e2) 则 e1.hashCode()==e2.hashCode()
*/
int hashCode();
// JDK 1.8 起提供了四个默认实现的比较方法
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());
}
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());
}
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());
}
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());
}
}
其他 API
// Query Operations
/**
* 返回此 Map 键值对映射的数量,最大值为 Integer.MAX_VALUE
*/
int size();
/**
* 如果 此Map 没有键值对映射返回 true
*/
boolean isEmpty();
/**
* 如果包含指定 key 的映射则返回 true,即满足如下情况返回true :
* (key==null ? k==null : key.equals(k))
*
* @throws 如果指定的 key 与 Map 不匹配抛出 ClassCastException
* @throws 如果指定的 key 是 null 且此 Map 不支持 null 抛出 NullPointerException
*/
boolean containsKey(Object key);
/**
* 如果此 Map 存在多个 key 值的 value 与指定 value 相等则返回 true.
*
* @throws 如果指定的 key 与 Map 不匹配抛出 ClassCastException
* @throws 如果指定的 key 是 null 且此 Map 不支持 null 抛出 NullPointerException
*/
boolean containsValue(Object value);
/**
* 返回指定 key 的 value 值,如果不存在指定的 key 返回 null。
* 如果映射允许空值,那么返回 null 不一定代表不存在指定的映射,有可能映射的 value 就是 null,
* 可以通过 containsKey 方法判断
* @throws 如果指定的 key 与 Map 不匹配抛出 ClassCastException
* @throws 如果指定的 key 是 null 且此 Map 不支持 null 抛出 NullPointerException
*/
V get(Object key);
// Modification Operations
/**
* 将指定值与指定键相关联。如果映射先前包含键的映射,则旧值将替换为指定的值。
* (当且仅当 m.containsKey(k) 将返回true时)
*
* @return 与键关联的上一个值,如果之前没有此键的映射则返回 null
* @throws 如果不支持 put 操作则抛出 UnsupportedOperationException
* @throws 如果指定的 key 或 value 不匹配当前 Map 抛出 ClassCastException
* @throws 如果指定的 key 或 value 为 null 且当前 Map 不支持 null key 或者 null value 抛出 NullPointerException
* @throws 如果指定的 key 或者 value 的属性阻止放入此 Map 则抛出 IllegalArgumentException
*/
V put(K key, V value);
/**
* 移除与指定的 key 匹配的映射,如果此映射存在的话,并返回移除的 value 值,
* 如果不存在匹配的映射返回 null,如果 Map 支持 null 值,且 key 对应的 value
* 就是 null 也可能返回 null
* 匹配一次就直接返回,因为 Map 不允许重复的 key
*
* @throws 如果不支持 remove 操作则抛出 UnsupportedOperationException
* @throws 如果指定的 key 与 Map 不匹配抛出 ClassCastException
* @throws 如果指定的 key 是 null 且此 Map 不支持 null 抛出 NullPointerException
*/
V remove(Object key);
// Bulk Operations
/**
* 复制指定的 Map 中的映射到此 Map
* 相当于给指定的 Map 中的每个映射执行了一次 put 操作
*
* @throws 如果不支持 putAll 操作则抛出 UnsupportedOperationException
* @throws 如果指定的 Map 中的 key 或 value 不匹配当前 Map 抛出 ClassCastException
* @throws 如果指定的 Map 中的 key 或 value 为 null 且当前 Map 不支持 null key 或者 null value 抛出 NullPointerException
* @throws 如果指定的 Map 中的 key 或者 value 的属性阻止放入此 Map 则抛出 IllegalArgumentException
*/
void putAll(Map<? extends K, ? extends V> m);
/**
* 移除所有映射
*
* @throws 如果不支持 clear操作则抛出 UnsupportedOperationException
*/
void clear();
// Views
/**
* 返回 key 的 Set 集合
*
* @return a set view of the keys contained in this map
*/
Set<K> keySet();
/**
* 返回 value 的 集合
*
* @return a collection view of the values contained in this map
*/
Collection<V> values();
/**
* 返回 key-value 键值对的映射集合
*
* @return a set view of the mappings contained in this map
*/
Set<Map.Entry<K, V>> entrySet();
interface Entry<K,V> {
// ...
}
// Comparison and hashing
/**
* 将指定的对象与此 Map 比较,如果 m1.entrySet().equals(m2.entrySet()) 返回 true
*/
boolean equals(Object o);
/**
* 返回此 Map 的哈希值,是由每个 Entry 的哈希值累加得到的
*/
int hashCode();
// Defaultable methods
/**
* 如果没有指定的 key 则返回默认的 value
* @since 1.8
*/
default V getOrDefault(Object key, V defaultValue) {
V v;
return (((v = get(key)) != null) || containsKey(key))
? v
: defaultValue;
}
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);
}
}
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);
}
// ise thrown from function is not a cme.
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);
}
}
}
/**
* 如果映射不存在或者 value 本身就是 null 则替换 value 值
*/
default V putIfAbsent(K key, V value) {
V v = get(key);
if (v == null) {
v = put(key, value);
}
return v;
}
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;
}
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;
}
default V replace(K key, V value) {
V curValue;
if (((curValue = get(key)) != null) || containsKey(key)) {
curValue = put(key, value);
}
return curValue;
}
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;
}
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;
}
}
AbstractMap
对应于AbstractCollection
,AbstractMap
的作用也是类似的,主要是针对 Map 提供一些方法的通用实现。如果需要实现一个不可修改的 Map,只需要继承 AbstractMap 并重写 entrySet 方法。entrySet 方法返回的 Set 集合不应该支持 add、remove 方法,其迭代器也不应该支持 remove 方法。要实现可修改的 Map,必须重写此类的 put 方法(AbstractMap 默认抛出 UnsupportedOperationException 异常)和 entrySet 方法。iterator 方法返回的迭代器必须实现 remove 方法。
entrySet 方法在 AbstractMap 中仍然是一个抽象方法。
public abstract Set<Entry<K,V>> entrySet();
另外在 AbstractMap 中定义了两个变量,这两个变量在 AbstractMap 的子类 HashMap、TreeMap 都有用到。被用来存储返回的集合视图。
// 存储 key 集合
transient Set<K> keySet;
// 存储 values
transient Collection<V> values;
其他方法
public abstract class AbstractMap<K,V> implements Map<K,V> {
/**
* 仅供子类调用的构造器
*/
protected AbstractMap() {
}
// Query Operations
/**
* 使用 entrySet().size() 返回当前 Map 的容量
*/
public int size() {
return entrySet().size();
}
/**
* entrySet().size() == 0 表示是空的
*/
public boolean isEmpty() {
return size() == 0;
}
/**
* 使用 entrySet() 返回的 Set 集合的迭代器遍历。对 null 和 非 null 值进行了区分对待
*
* @throws ClassCastException
* @throws NullPointerException
*/
public boolean containsValue(Object value) {
Iterator<Entry<K,V>> i = entrySet().iterator();
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;
}
/**
* 与上方法相似
*
* @throws ClassCastException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
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();
if (key.equals(e.getKey()))
return true;
}
}
return false;
}
/**
* 与上方法相似
*
* @throws ClassCastException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
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;
}
// Modification Operations
/**
* 默认抛出异常
*
* @throws UnsupportedOperationException {@inheritDoc}
* @throws ClassCastException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
* @throws IllegalArgumentException {@inheritDoc}
*/
public V put(K key, V value) {
throw new UnsupportedOperationException();
}
/**
* 通过 entrySet().iterator() 找到匹配的 key 然后通过迭代器删除
* 如果键值对较多则效率并不高,时间复杂度是随着键值对数量线性增长的
*
* @throws UnsupportedOperationException {@inheritDoc}
* @throws ClassCastException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
public V remove(Object key) {
Iterator<Entry<K,V>> i = entrySet().iterator();
Entry<K,V> correctEntry = 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;
if (correctEntry !=null) {
// 上面调过 next 方法这里调 remove 方法不会抛出异常
oldValue = correctEntry.getValue();
i.remove();
}
return oldValue;
}
// Bulk Operations
/**
* 将指定的 Map 中的 键值对映射添加到此 Map 中
* 这里使用的是 entrySet 增强 for 循环一个个添加
*
* @throws UnsupportedOperationException {@inheritDoc}
* @throws ClassCastException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
* @throws IllegalArgumentException {@inheritDoc}
*/
public void putAll(Map<? extends K, ? extends V> m) {
for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
put(e.getKey(), e.getValue());
}
/**
* 通过 entrySet().clear() 方法清空键值对,如果不支持此操作抛出 UnsupportedOperationException
*
* @throws UnsupportedOperationException
*/
public void clear() {
entrySet().clear();
}
/**
* 返回 key 的 Set 集合
* 这里没有采取遍历 entrySet() 取出 key 的方式是考虑到如果数据较多时的性能影响,
* 这里重新定义了一个 Set 并重写了 Iterator,巧妙的避免了遍历 Entry
*/
public Set<K> keySet() {
Set<K> ks = keySet;
if (ks == null) {
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;
}
/**
* 返回 values
*/
public Collection<V> values() {
Collection<V> vals = values;
if (vals == null) {
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;
}
public abstract Set<Entry<K,V>> entrySet();
// Comparison and hashing
/**
* 将指定的对象与此 Map 进行相等性比较
*/
public boolean equals(Object o) {
// 判断是不是本身
if (o == this)
return true;
// 是不是 Map 类型
if (!(o instanceof Map))
return false;
Map<?,?> m = (Map<?,?>) o;
// 长度不相等返回 false
if (m.size() != size())
return false;
try {
// entrySet() 获取迭代器遍历当前 Map
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;
}
/**
* 返回 Map 的哈希码
*/
public int hashCode() {
int h = 0;
Iterator<Entry<K,V>> i = entrySet().iterator();
// 累加每个 Entry 的 哈希码
while (i.hasNext())
h += i.next().hashCode();
return h;
}
/**
* 重写 toString 方法
*
*/
public String toString() {
Iterator<Entry<K,V>> i = entrySet().iterator();
if (! i.hasNext())
// 空 Map 返回 {}
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);
// 使用 = 连接 key 和 value
sb.append('=');
sb.append(value == this ? "(this Map)" : value);
if (! i.hasNext())
return sb.append('}').toString();
// 使用逗号分隔 Entry
sb.append(',').append(' ');
}
}
/**
* 返回一个 AbstractMap 实例的快照,不克隆 keys 和 values
*
* @return a shallow copy of this map
*/
protected Object clone() throws CloneNotSupportedException {
AbstractMap<?,?> result = (AbstractMap<?,?>)super.clone();
result.keySet = null;
result.values = null;
return result;
}
}
在 Map 内部定义了 一个 Entry 接口,AbstractMap 提供了两个 Entry 实现,一个是可变的 SimpleEntry 和一个不可变的SimpleImmutableEntry。
SimpleEntry
SimpleEntry 对于 key 值的定义是一个 final 修饰意味着是一个不可变的引用。
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;
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;
}
public V setValue(V value) {
V oldValue = this.value;
this.value = value;
// 返回旧值
return oldValue;
}
public boolean equals(Object o) {
//判断参数是否是Map.Entry类型,要equals相等首先得是同一个类型
if (!(o instanceof Map.Entry))
return false;
//将Object类型强转为Map.Entry类型
// 这里参数使用“?”而不是“K, V”是因为泛型在运行时类型会被擦除,编译器不知道K,V具体是什么类型
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
//key和value分别调用eq方法进行判断,都返回ture时equals才相等。
return eq(key, e.getKey()) && eq(value, e.getValue());
}
public int hashCode() {
// 对 key 和 value 的哈希码进行异或运算
return (key == null ? 0 : key.hashCode()) ^
(value == null ? 0 : value.hashCode());
}
}
eq 方法是 AbstractMap 的一个私有方法,注意这里使用的是 Object 类型,Object 类型的 equals 方法使用的 == 判断。
private static boolean eq(Object o1, Object o2) {
return o1 == null ? o2 == null : o1.equals(o2);
}
SimpleImmutableEntry
SimpleImmutableEntry 的 key 和 value 属性都是 final 修饰的,且 set 方法默认抛出 UnsupportedOperationException 异常,所以 Entry 是不可变的。这两点是和 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;
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;
}
/**
* 抛出 UnsupportedOperationException
*/
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());
}
}