书接上文,上一篇中对 Set 接口的最终实现类 TreeSet 进行了介绍与分析,本篇开始将对 Map 接口进行分析。还是先来看下数据结构总继承关系图与 Map 部分的继承关系图:
可以看到 Map 与 Collection 数据结构本身是没有关系的,但是上一个对于 Set 接口的介绍篇章中可以看到,无论是实现了 Collection 特性的 HashSet 还是 TreeSet 其实都只是在操作 HashMap 和 TreeMap 而已,所以可以认为这是一种桥接模式,Map 与 Collection 完全解耦,但是却提供了实现 Collection 各种能力的方式。
还是首先来看顶端接口 Map
/**
* map 的原本翻译为映射,为了避免歧义,以下 map 作为数据结构出现时不翻译,作为键值映射出现时翻译为简直映射,
* 而 entry 则翻译成条目,只有在源码中写明 key-value pair 的地方才会翻译成键值对,view 则翻译成视图,这
* 样翻译是最精确的
*
* 一个映射键与值关系的对象。一个 map 不能包含相同的键,每个键可以映射至多一个值。
*
* 这个接口取代了 Dicionary 类的位置,它(Dictionary)完全是一个抽象类而不是一个接口。
*
* 映射接口提供三种数据结构试图,这允许一个映射的内容可以是一个 keys set 的视图,值的数据结
* 构集,或者键值映射的 set。一个映射的顺序被定义为与映射数据结构视图的串行迭代器中返回的
* 元素的顺序相同。一些映射实现类,像是 TreeMap 类,对它们的顺序做出了特定的保证;另外一
* 些,像是 HashMap 类,则没有。
*
* 注意:如果可变的对象用作 map 的 key 时需要特别注意。当一堆对象是 map 的键时,如果它被以
* 影响了 equals 比较行为的方式被更改了,map 是不会发现的。这种禁令的一种特殊情况是 map
* 不可能将它自己作为键。当 map 可以将自己作为值时,建议提高警惕:equals 和 hashCode 方
* 法不再在这个 map 中被完好的定义。
*
* 所有一般目的的 map 实现类应该提供两种“标准”的构造器:一个是空(没有参数)的构造器,构建一
* 个空的 map,还有一种是带有一个 Map 类型参数的构造器,构建一个与参数的映射关系完全相同
* 的新 map。其实,后一种构造器允许用户来拷贝任何 map,产生一个与需要的类型相同的 map。没
* 有方式可以强制执行这种建议(因为接口不能包含构造器)但是所有 JDK 中一般目的的 map 实现类
* 都遵从这种建议。
*
* “销毁”方法包含在这个接口中,意味着,对于改动 map 的方法,如果 map 不支持这种操作,就会
* 抛出 UnsupportedOperationException。如果是这种情况,这些方法可以,但不是强制的,如果
* 调用在 map 上没有副作用的话,抛出一个 UnsupportedOperationException。比如,在一个不
* 可改动的 map 上调用 {@link #putAll(Map)} 方法可能,但不是强制的,在 map 的映射要
* 去“叠加”空时候抛出异常。
*
* 一些 map 实现类它们包含的对键与值有限制。比如,一些实现类禁止 null 键和值,还有一些对于
* 它们的键类型有限制。试图插入一个不合规的键或者值抛出一个 unchecked 异常,典型的是
* NullPointerException 或者 ClassCastException。试图查询一个不合规的键或者值可能跑出
* 一个异常,或者它简单的返回 false;一些实现类将显示前者的行为而一些则会显示后者的。更一般
* 地说,假设在一个结束时不会返回一个不合规元素结果的不合规的键或者值上的操作会抛出一个异常或
* 者它可能成功,这是实现类可以选择的。类似的异常在这个接口规范中被标记为“可选的”。
*
* 许多 Collections 框架接口中的方法被从 {@link Object#equals(Object) equals} 方法
* 角度定义。比如,{@link #containsKey(Object) containsKey(Object key)} 方法这样描
* 述:“当且仅当这个 map 包含一个 key 为 k 的遵循
* (key==null ? k==null : key.equals(k))
* 的映射时候返回 true。”这个规范不应该被理解成有这样的隐藏含义:用一个不为 null 的参数键
* 调用 Map.containsKey 将导致 key.equals(k) 被任何键 k 调用。实现类可以自由的优化,从
* 而调用 equals,比如,首先比较两个键的哈希值。({@link Object#hashCode()} 规范保证了
* 两个 hash codes 不相等的对象不可能 equal。)更一般化地说,各种集合框架接口的实现类可以
* 在认为合适的时候自由地利用底层的指定行为的 {@link Object} 的方法。
*
* 一些执行 map 递归遍历的映射操作可能会在 map 直接或者间接的包含自身时候失败,伴随一个与自
* 我参照实例相关的异常。这包括 {@code clone()}, {@code equals()}, {@code
* hashCode()} and {@code toString()} 方法。实现类可以有选择的解决自我参照情景,但是,
* 大部分现有的实现类不那么做。
*/
public interface Map<K,V> {
// 查询操作
/**
* 返回 map 中的键值映射对数量。如果 map 中包含的元素笔 Integer.MAX_VALUE 多,返回
* Integer.MAX_VALUE。
*/
int size();
/**
* 如果当前 map 中不包含任何键值映射关系,则返回 true。
*/
boolean isEmpty();
/**
* 如果当前 map 包含指定键的键值对,则返回 true。更正式地说,当且仅当这个 map 包含类
* 似于 (key==null ? k==null : key.equals(k)) 这样的 k 作为 key 时返回 true。
* (最多只能有一个这样的映射。)
*/
boolean containsKey(Object key);
/**
* 如果当前 map 映射了一个或多个键在指定值上。更正式的说,当且仅当当前 map 包含至少一
* 个对于值 v 来说有类似 (value==null ? v==null : value.equals(v)) 的映射。这个
* 操作的时间对于大部分 Map 接口的实现类来说将大致与 map 的长度成线性关系。
*/
boolean containsValue(Object value);
/**
* 返回指定 key 映射的 值,或者如果当前 map 不包含键对应的映射则返回 {@code null}。
*
* 更正式地说,如果 map 包含一个类似于 {@code (key==null ? k==null :
* key.equals(k))} 从 key 到 value 的映射,那么这个方法返回 {@code v};否则它返
* 回 {@code null}。(至多只会存在一个这样的映射。)
*
* 如果 map 允许 null 值,那么一个 {@code null} 的返回值对于表明 map 中不包含 key
* 的映射就是不必要的了;它也有可能是 map 明确有键映射到 {@code null}。
* {@link#containsKey containsKey} 操作可以用来区分这两种情况。
*/
V get(Object key);
// 修改操作
/**
* 将当前 map 中的指定值与指定键关联(可选操作)。如果 map 之前包含了一个键的映射,旧
* 值将被指定值替代。(当且仅当一个 map m 对于 k 键包含类似于 {@link
* #containsKey(Object) m.containsKey(k)} 返回 true 的关系。)
*/
V put(K key, V value);
/**
* 从 map 中移除一个键的映射关系,如果它存在的话(可选操作)。更正式地说,如果这个 map
* 包含一个以 k 为键,v 为值的类似于 (key==null ? k==null : key.equals(k)) 映
* 射关系,这个映射会被移除。(map 值能包含至多一种这样的映射。)
*
* 返回 map 中之前与这个键关联的值,或者如果 map 中不包含这个键对应的映射则返回
* null。
*
* 如果 map 允许 null 值,那么一个 {@code null} 的返回值对于表明 map 中不包含 key
* 的映射就是不必要的了;它也有可能是 map 明确有键映射到 {@code null}。
*
* 一旦调用返回后 map 将不再包含指定的键。
*/
V remove(Object key);
// 扩展操作
/**
* 从指定的 map 中拷贝所有的映射到当前 map 中(可选操作)。这个调用的效果和在当前 map
* 中对指定 map 中一个一个键值对调用 {@link #put(Object,Object) put(k, v)} 的效
* 果是一样的。如果指定 map 在操作过程中被改动了,这种操作行为就是不可定义的。
*/
void putAll(Map<? extends K, ? extends V> m);
/**
* 移除当前 map 所有的键值对映射(可选操作)。调用后 map 将会为空。
*/
void clear();
// 视图
/**
* 返回一个包含当前 map 中所有键的 {@link Set} 视图。set 是被 map 支持的,所以对于
* map 的修改也会影响到 set,反之亦然。如果在一个覆盖 set 的迭代操作执行过程中 map 被
* 修改了(以通过串行迭代器自己的 remove 操作之外的方式),迭代操作的结果是不可定义的。
* set 支持元素移除,它对应移除 map 对应的键值对映射,通过 Iterator.remove,
* Set.remove,removeAll,retainAll 和 clear 操作。它不支持 add 或者 addAll 操
* 作。
*/
Set<K> keySet();
/**
* 返回一个当前 map 中包含的值的 {@link Collection} 视图。这个 collection 是被 map 支持的,所
* 以对于 map 的修改也会影响到 collection,反之亦然。如果在一个覆盖 collection 的迭代操作执行过
* 程中 map 被修改了(以通过串行迭代器自己的 remove 操作之外的方式),迭代操作的结果是不可定义的。
* collection 支持元素移除,它对应移除 map 对应的键值对映射,通过 Iterator.remove,
* Collection.remove,removeAll,retainAll 和 clear 操作。它不支持 add 或者 addAll 操作。
*/
Collection<V> values();
/**
* 返回一个当前 map 中包含的映射键值对的 {@link Set} 视图。这个 set 被 map 支持,所以对于 map
* 的改动也会影响 set,反之亦然。如果在一个覆盖 collection 的迭代操作执行过程中 map 被修改了(以通
* 过串行迭代器自己的 remove 操作之外的方式,或者对一个串行迭代器返回的 map entry 进行 setValue
* 操作),迭代操作就是不可定义的。set 支持元素移除,它对应移除 map 对应的键值对映射,通过
* Iterator.remove,Set.remove,removeAll,retainAll 和 clear 操作。它不支持 add 或者
* addAll 操作。
*/
Set<Map.Entry<K, V>> entrySet();
/**
* 一个 map 条目(键值对)。Map.entrySet 方法返回 map 的一个 collection 视图,它的元素就是这个类。
* 唯一包含一个引用到一个 map 条目的方式是通过迭代这个 collection 视图。这些 Map.Entry 对象仅仅为了
* 持续迭代操作存在;更正式地说,如果在条目被迭代器返回后,支持的 map 被修改了,那么一个 map 条目的行为
* 就是不可定义的,除非通过 map 条目的 setValue 来操作。
*
* 条目接口
*/
interface Entry<K,V> {
/**
* 返回这个条目对应的键。
*/
K getKey();
/**
* 返回这个条目对应的值。如果映射被从支持的 map 中移除了(通过迭代器的 remove 操作),这个调用的结
* 果就是不可定义的。
*/
V getValue();
/**
* 用指定值替换这个条目对应的值(可选操作)。(通过 map 写。)如果映射关系已经从 map 中移除(通过
* 迭代器的 remove 操作)了,那么这个调用行为是不可定义的。
*/
V setValue(V value);
/**比较当前条目与指定对象的相等性。如果给定对象也是一个 map 条目并且两个条目代表了相同的映射关系则
* 返回 true。更正式地说,两个条目 e1 和 e2 如果满足
* (e1.getKey()==null ?
* e2.getKey()==null : e1.getKey().equals(e2.getKey())) &&
* (e1.getValue()==null ?
* e2.getValue()==null : e1.getValue().equals(e2.getValue()))
*
* 就代表它们是相同的映射。这保证了 Map.Entry 接口的不同实现类都能通过 equals 方法正确地工作。
*/
boolean equals(Object o);
/**
* 返回当前 map 条目的 hash 值。一个 map 条目 e 的 hash 值定义为:
* (e.getKey()==null ? 0 : e.getKey().hashCode()) ^
* (e.getValue()==null ? 0 : e.getValue().hashCode())
*
* 这保证了对于任意两个条目 e1 和 e2,e1.equals(e2) 隐式的包含了
* e1.hashCode()==e2.hashCode() ,就像 Object.hashCode 的普遍规约那样。
*/
int hashCode();
/**
* 返回一个通过键的自然顺序比较 {@link Map.Entry} 的 comparator。
*/
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());
}
/**
* 返回一个通过值的自然顺序比较 {@link Map.Entry} 的 comparator。
*/
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());
}
/**
* 返回一个使用给定 {@link Comparator} 作为键的顺序来比较 {@link Map.Entry} 的 comparator。
*/
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()); //函数式调用,c1,c2 都是 map 的条目,比较它们的键
}
/**
* 返回一个使用给定 {@link Comparator} 作为值的顺序来比较 {@link Map.Entry} 的 comparator。
*/
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()); //函数式调用,c1,c2 都是 map 的条目,比较它们的值
}
}
// 比较和哈希
/**
* 比较给定对象与当前 map 的相等性。如果给定对象也是一个 map 并且两个 map 提供了相同的键值映射则返回
* true。更正式地说,两个 maps m1 和 m2 如果满足
* m1.entrySet().equals(m2.entrySet())
* 则表示它们是相同的。这保证了在 Map 接口不同的实现类中 equals 方法都可以正常地工作
*/
boolean equals(Object o);
/**
* 返回当前 map 的 hash 值。一个 map 的 hash 值是由 map 的 entrySet 视图中的每个条目的 hash 值累
* 加得到的。这保证了对于任意两个 maps m1 和 m2,m1.equals(m2) 隐式的包含了 m1.hashCode() ==
* m2.hashCode(),就像 {@link Object#hashCode} 普遍规约中要求的。
*/
int hashCode();
// 默认类型的方法
/**
* 返回给定键映射的值,或者如果这个 map 中不包含这个键的映射关系,则返回 {@code defaultValue}
* Returns the value to which the specified key is mapped, or
* {@code defaultValue} if this map contains no mapping for the key.
*
* 实现细节
* 默认实现类不保证线程安全或者这个方法属性的原子性。任何提供原子性保证的实现类必须重写这个方法和标记它
* 的并发属性。
*/
default V getOrDefault(Object key, V defaultValue) {
V v;
return (((v = get(key)) != null) || containsKey(key))
? v
: defaultValue;
}
/**
* 为每个当前 map 中的条目执行指定的消费动作指导所有的条目都被处理或者消费动作抛出一个异常。除非这个实现
* 那类指定其他特定的,消费动作按照条目 set 迭代动作来执行(如果一个迭代顺序是特定的。)消费动作引发的异
* 常将传递到调用者。
*
* 实现细节
* 默认实现等价于,对于当前 {@code map}:
* {@code
* for (Map.Entry entry : map.entrySet())
* action.accept(entry.getKey(), entry.getValue());
* }
*
* 默认实现类不保证线程安全或者这个方法属性的原子性。任何提供原子性保证的实现类必须重写这个方法和标记它
* 的并发属性。
*/
default void forEach(BiConsumer<? super K, ? super V> action) {
Objects.requireNonNull(action);
for (Map.Entry<K, V> entry : entrySet()) { //通过 for 循环 entrySet 中的条目
K k;
V v;
try {
k = entry.getKey(); //缓存条目的键
v = entry.getValue(); //缓存条目的值
} catch(IllegalStateException ise) {
/**这通常意味着条目不再存在在 map 中**/
throw new ConcurrentModificationException(ise);
}
action.accept(k, v); //逐个消费键,值
}
}
/**
* 在条目上调用指定 function 替换每个条目的值,直到所有条目都被处理了或者 function 抛出一个异常。异常
* 将传递到调用者
*
* 实现细节
* 默认实现类等价于,对于当前 {@code map}
* {@code
* for (Map.Entry entry : map.entrySet())
* entry.setValue(function.apply(entry.getKey(), entry.getValue()));
* }
*
* 默认实现类不保证线程安全或者这个方法属性的原子性。任何提供原子性保证的实现类必须重写这个方法和标记它
* 的并发属性。
*/
default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
Objects.requireNonNull(function);
for (Map.Entry<K, V> entry : entrySet()) { //通过 for 循环 entrySet 中的 条目
K k;
V v;
try {
k = entry.getKey(); //缓存条目的键
v = entry.getValue(); //缓存条目的值
} catch(IllegalStateException ise) {
/**这通常意味着条目不再存在在 map 中**/
throw new ConcurrentModificationException(ise);
}
/**funcitn 抛出的 ise 不是一个 cms(这一句原来就不知道是什么意思。。)**/
v = function.apply(k, v);
try {
entry.setValue(v); //条目重新设值
} catch(IllegalStateException ise) {
/**这通常意味着条目不再存在在 map 中**/
throw new ConcurrentModificationException(ise);
}
}
}
/**
* 如果指定键不再与一个值相关(或者被映射到 {@code null}),将它与指定值相连并返回 {@code null},否
* 则返回当前值。
*
* 实现细节
* 默认实现类等价于,对于当前 {@code map}:
* {@code
* V v = map.get(key);
* if (v == null)
* v = map.put(key, value);
* return v;
* }
*
* 默认实现类不保证线程安全或者这个方法属性的原子性。任何提供原子性保证的实现类必须重写这个方法和标记它
* 的并发属性。
*/
default V putIfAbsent(K key, V value) {
V v = get(key); //调用 get 方法,缓存结果为 v
if (v == null) { //如果 v 为 null
v = put(key, value); //调用 put 方法,结果赋值给 v
}
return v; //返回 v
}
/**
* 只有当指定键当前映射到指定值时将条目移除。
*
* 实现细节
* 默认实现类等价于,对于当前 {@code map}:
* {@code
* if (map.containsKey(key) && Objects.equals(map.get(key), value)) {
* map.remove(key);
* return true;
* } else
* return false;
* }
*
* 默认实现类不保证线程安全或者这个方法属性的原子性。任何提供原子性保证的实现类必须重写这个方法和标记它
* 的并发属性。
*/
default boolean remove(Object key, Object value) {
Object curValue = get(key); //调用 get 方法,缓存结果为 curValue
if (!Objects.equals(curValue, value) || //调用 Objects.equals 比较 curValue 与 value 的相等性
(curValue == null && !containsKey(key))) { //或者 curValue 为 null 并且 containsKey 返回 false
return false; //返回 false
}
remove(key); //调用 remove 方法
return true; //返回 true
}
/**
* 只有当指定键当前映射到指定值时候将条目提花。
*
* 实现细节
* 默认实现类定价与,对于当前 {@code map}:
* {@code
* if (map.containsKey(key) && Objects.equals(map.get(key), value)) {
* map.put(key, newValue);
* return true;
* } else
* return false;
* }
*
* 如果旧值是 null 除非新值也是 null,默认实现类对于那些不支持 null 值的 maps 才不会抛出
* NullPointerException。
*
* 默认实现类不保证线程安全或者这个方法属性的原子性。任何提供原子性保证的实现类必须重写这个方法和标记它
* 的并发属性。
*/
default boolean replace(K key, V oldValue, V newValue) {
Object curValue = get(key);
if (!Objects.equals(curValue, oldValue) || //调用 Objects.equals 方法比较 curValue 和 oldValue 的相等性
(curValue == null && !containsKey(key))) { //判断 curValue 是否为 null 并且 containsKey 返回值是否为 false
return false; //返回 false
}
put(key, newValue); //调用 put 方法
return true; //返回 true
}
/**
* 只有当指定键当前映射到某个值时替换条目。
*
* 实现细节
* 默认实现类等价于 {@code map}:
* {@code
* if (map.containsKey(key)) {
* return map.put(key, value);
* } else
* return null;
* }
*
* 默认实现类不保证线程安全或者这个方法属性的原子性。任何提供原子性保证的实现类必须重写这个方法和标记它
* 的并发属性。
*/
default V replace(K key, V value) {
V curValue;
if (((curValue = get(key)) != null) || containsKey(key)) { //调用 get 方法缓存给 curValue 并判断它是否不为 null 或者 调用 containKey 方法判断是否为 ture
curValue = put(key, value); //调用 put 方法,赋值给 curValue
}
return curValue; //返回 curValue
}
/**
* 如果指定键还没有关联到一个值(或者映射到 {@code null}),尝试使用给定的映射 function 计算它的值并
* 且将它输入到当前 map 除非是 {@code null}。
*
* 如果 function 返回 {@code null} 那么没有映射关系被记录。如果 functino 自己抛出一个(未经检查
* 的)异常,异常将被重新抛出,而且没有映射关系被记录。最常用的使用方式是构造一个新的对象作为初始的被映射
* 的值或者缓存结果,就像在:
* {@code
* map.computeIfAbsent(key, k -> new Value(f(k)));
* }
* 或者来实现一个多值map,{@code Map>},支持每个键对应多个值:
* {@code
* map.computeIfAbsent(key, k -> new HashSet()).add(v);
* }
*
* 实现细节
* 默认实现类等价于,对于当前 {@code map} 使用以下步骤,然后返回当前值或者 {@code null},如果现在存
* 在不存在的话:
* {@code
* if (map.get(key) == null) {
* V newValue = mappingFunction.apply(key);
* if (newValue != null)
* map.put(key, newValue);
* }
* }
*
* 默认实现类不保证线程安全或者这个方法属性的原子性。任何提供原子性保证的实现类必须重写这个方法和标记它
* 的并发属性。特别需要指出的是,所有子接口 {@link java.util.concurrent.ConcurrentMap} 的实现类
* 必须标记 ,无论 funciton 是否只在值不存在的时候自动应用一次。
*/
default V computeIfAbsent(K key,
Function<? super K, ? extends V> mappingFunction) {
Objects.requireNonNull(mappingFunction);
V v;
if ((v = get(key)) == null) { //调用 get 方法,赋值给 v,然后判断 v 是否为 null
V newValue;
if ((newValue = mappingFunction.apply(key)) != null) { //调用 mappingFunction.apply 方法并赋值给 newValue,判断 newValue 是否不为 null
put(key, newValue); //调用 put 方法
return newValue; //返回 newValue
}
}
return v; //返回 v
}
/**
* 如果指定键对应的值存在并且不是 null,试图计算一个给定键与它当前映射值的新的映射关系。
*
* 如果 function 返回 {@code null},映射关系将被移除。如果 function 自身抛出一个(未经检查的)异
* 常,异常将被重新抛出,并且当前映射关系将不变。
*
* 实现细节
* 默认实现类等价于对于当前 {@code map} 执行以下步骤,然后返回当前值或者如果当前不存,返回 {@code
* null} :
* {@code
* if (map.get(key) != null) {
* V oldValue = map.get(key);
* V newValue = remappingFunction.apply(key, oldValue);
* if (newValue != null)
* map.put(key, newValue);
* else
* map.remove(key);
* }
* }
*
* 默认实现类不保证线程安全或者这个方法属性的原子性。任何提供原子性保证的实现类必须重写这个方法和标记它
* 的并发属性。需要特别指出的是,所有子接口 {@link java.util.concurrent.ConcurrentMap} 的实现类
* 必须标记 ,无论 funciton 是否只在值不存在的时候自动应用一次。
*/
default V computeIfPresent(K key,
BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
Objects.requireNonNull(remappingFunction);
V oldValue;
if ((oldValue = get(key)) != null) { //调用 get 方法,返回值赋值给 oldValue,并判断 oldValue 是否不为 null
V newValue = remappingFunction.apply(key, oldValue); //调用 remappingFunction.apply 方法,返回值赋值给 newValue
if (newValue != null) { //如果 newValue 不为 null
put(key, newValue); //调用 put 方法
return newValue; //返回 newValue
} else { //如果 newValue 是 null
remove(key); //调用 remove 方法
return null; //返回 null
}
} else { //如果 oldValue 为 null
return null; //返回 null
}
}
/**
* 视图计算一个指定键与它的现有映射值(或者如果没有当前映射,那么是 {@code null})的映射关系。比如,为
* 值创建或者追加一个 {@code String} 消息映射:
* {@code
* map.compute(key, (k, v) -> (v == null) ? msg : v.concat(msg))}
* (方法 {@link #merge merge()} 通常对于这种目的来说是更简便的用法。)
*
* 如果 function 返回 {@code null},映射将被移除(或者如果一开始就不存在,就保持不存在)。如果
* function 自己抛出一个(未经检查的)异常,异常将被重新抛出,并且当前映射将保持不变。
*
* 实现细节
* 默认实现类等价于当前 {@code map} 执行以下操作,然后返回当前值或者如果不存在,则返回 {@code
* null}:
* {@code
* V oldValue = map.get(key);
* V newValue = remappingFunction.apply(key, oldValue);
* if (oldValue != null ) {
* if (newValue != null)
* map.put(key, newValue);
* else
* map.remove(key);
* } else {
* if (newValue != null)
* map.put(key, newValue);
* else
* return null;
* }
* }
*
* 默认实现类不保证线程安全或者这个方法属性的原子性。任何提供原子性保证的实现类必须重写这个方法和标记它
* 的并发属性。需要特别指出的是,所有子接口 {@link java.util.concurrent.ConcurrentMap} 的实现类
* 必须标记 ,无论 funciton 是否只在值不存在的时候自动应用一次。
*/
default V compute(K key,
BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
Objects.requireNonNull(remappingFunction);
V oldValue = get(key); //调用 get 返回,缓存结果给 oldValue
V newValue = remappingFunction.apply(key, oldValue); //调用 remappingFunction.apply 方法,结果缓存给 newValue
if (newValue == null) { //判断 newValue 是否为 null
/**删除映射关系**/
if (oldValue != null || containsKey(key)) { //判断 oldValue 是否不为 null 并且 containsKey 返回 true
/**移除一些东西**/
remove(key); //调用 remove 方法
return null; //返回 null
} else {
/**不做任何事**/
return null; //返回 null
}
} else { //如果 newValue 不是 null
/**添加或者替换旧映射关系**/
put(key, newValue); //调用 put 方法
return newValue; //返回 newValue
}
}
/**
* 如果指定 key 还没有与一个值关联或者与 null 关联,将它与一个给定的非 null 值关联。或者,用给定重新映
* 射 function 的返回值替换当前关联的值,如果如果结果是 {@code null} 就移除。这个方法可能在为一个键
* 合并多个值时被用到。比如,创建或者拼接一个 {@code String msg} 到一个值映射:
* {@code
* map.merge(key, msg, String::concat)
* }
*
* 如果 function 返回 {@code null} 映射关系将被移除。如果 function 自身抛出一个(未经检查的)异
* 常,异常将被重新抛出,并且当前映射关系将不做更改。
*
* 实现细节
* 默认实现类等价于对当前 {@code map} 执行如下步骤,然后返回当前值或者当它不存在时返回 {@code
* null}:
* {@code
* V oldValue = map.get(key);
* V newValue = (oldValue == null) ? value :
* remappingFunction.apply(oldValue, value);
* if (newValue == null)
* map.remove(key);
* else
* map.put(key, newValue);
* }
*
*
* 默认实现类不保证线程安全或者这个方法属性的原子性。任何提供原子性保证的实现类必须重写这个方法和标记它
* 的并发属性。需要特别指出的是,所有子接口 {@link java.util.concurrent.ConcurrentMap} 的实现类
* 必须标记 ,无论 funciton 是否只在值不存在的时候自动应用一次。
*/
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); //调用 get 方法缓存给 oldValue
V newValue = (oldValue == null) ? value : //判断 oldValue 是否为 null,如果为 null,将 value 缓存给 newValue,否则调用 remappingFunction.apply 方法,将结果缓存给 newValue
remappingFunction.apply(oldValue, value);
if(newValue == null) { //如果 newValue 为 null
remove(key); //调用 remove 方法
} else { //如果 newValue 不为 null
put(key, newValue); //调用 put 方法
}
return newValue; //返回 newValue
}
}
从 Map 源码中可以看到 :
1、Map 提供了许多成对的方法(对于键和值提供成对的,功能相同的方法)。还有一些针对键的方法。
2、有一个很有趣的地方,之前在 Set 分析中已经发现,对于 Set 的所有操作其实就是对它维护的对应类型的 Map 操作,但是在 Map 中的 keySet,entrySet 方法返回的的又是 Set,目前在 Map 接口中还无反看到这两个方法的实现细节,但不难判断,这两个方法一定是通过 Set 实现类的某个构造方法对 Set 维护的 Map 变量进行了初始化,然后进而操作这两个方法返回的 Set 对象时,操作 Map 的对应方法,但是外部看起来是在操作一个 Set。
3、Map 中出现了一种新的接口 Entry(条目),这里千万不要将它与 Collection 接口实现类中的迭代器作用混淆,Entry 虽然与 Iterator 看上去都有迭代的功能,但是循环它的方式是通过 for 循环一个调用 entrySet() 返回的 Set 对象,而不是像迭代器那样包含所有元素与一个可以移动的游标。Entry 只是一个对于键值对概念简单的封装类。对于 Entry 的正确迭代方式是通过 entrySet() 返回 Set 对象后再调用这个 Set 对象的迭代器进行迭代。而由于引用的存在,所以通过 Set 的方法操作 Map 变量与直接通过 Map 方法操作当前 map 都会彼此影响。
4、最后 Map 针对 Java 8 的函数式接口以及流式计算提供了许多默认方法,对于原有功能的方法也添加了一些扩展。
由于 AbstractMap 抽象类是最终实现类 HashMap,SortedMap 与 ConcurrentHashMap 三个类共同的父类,所以也在这篇进行介绍,接下来来看 AbstractMap 的源码
/**
* 这个类提供了 Map 接口实现了的一个架构,来最小化实现这个接口需要的成本。
*
* 要实现一个不可改变的 map,开发者只需要继承这个类并提供一个 entrySet 方法的实现,返回一个 map 的映射关系
* 的 set 视图。典型的,返回的 set 将,反过来实现一个 AbstractSet 接口(此处源码笔误。。)。这个 set 不
* 应该支持 add 或者 remove 方法,而且它的迭代器不应该支持 remove 方法。
*
* 要实现一个可改变的 map,开发者必须额外重写这个类的 put 方法(否则会抛出一个
* UnsupportedOperationException),并且 entrySet().iterator() 方法返回的迭代器必须额外实现它的
* remove 方法。
*
* 开发者一般应该提供一个空(无参的)和一个带一个 Map 类型参数的构造器(这里源码又口胡了。。),就如在 Map
* 接口详述中推荐的那样。
*
* 这个类中每个非抽象方法的文档描述了它的实现细节。如果被实现的 map 需要一个更高效的实现方式,这样的方法可以
* 被重写。
*
* AbstractMap 抽象类实现了 Map 接口
* 对于继承文档的部分不再赘述
*/
public abstract class AbstractMap<K,V> implements Map<K,V> {
/**
* 唯一构造方法。(给子类隐式调用)
*/
protected AbstractMap() {
}
// 查询操作
/**
* {@inheritDoc}
*
* 实现细节
* 这个实现返回 entrySet().size()
*/
public int size() {
return entrySet().size(); //调用 entrySet().size(),返回结果
}
/**
* {@inheritDoc}
*
* 实现细节
* 这个实现返回 size() == 0
*/
public boolean isEmpty() {
return size() == 0; //调用 size() 方法并判断结果是否等于 0
}
/**
* {@inheritDoc}
*
* 实现细节
* 这个实现在 entrySet 上迭代查找一个带有指定值的条目。如果有一个条目被找到,返回 true。如果迭代操作结
* 束了都没有找到一个这样的条目,返回 false。注意这个实现的执行时间与 map 的长度成线性关系。
*/
public boolean containsValue(Object value) {
Iterator<Entry<K,V>> i = entrySet().iterator(); //调用 entrySet().iterator() 缓存给 i
if (value==null) { //如果参数不为 null
while (i.hasNext()) { //判断迭代器游标后是否还有值
Entry<K,V> e = i.next(); //步移游标返回下一个值,缓存给 e
if (e.getValue()==null) //如果条目 e 的值是否是 null
return true; //返回 true
}
} else { //如果参数为 null
while (i.hasNext()) { //判断迭代器游标后是否还有值
Entry<K,V> e = i.next(); //步移游标返回下一个值,缓存给 e
if (value.equals(e.getValue())) //如果条目 e 的值与 value 相等
return true; //返回 true
}
}
return false; //返回 false
}
/**
* {@inheritDoc}
*
* 实现细节
* 这个实现在 entrySet 上迭代查找一个带有指定键的条目。如果有一个条目被找到,返回 true。如果迭代操作结
* 束了都没有找到一个这样的条目,返回 false。注意这个实现的执行时间与 map 的长度成线性关系;许多实现类
* 都会重写这个方法。
*/
public boolean containsKey(Object key) {
Iterator<Map.Entry<K,V>> i = entrySet().iterator(); //调用 entrySet().iterator() 缓存给 i,注意这里的范型其实和 containsValue 方法中的不同,暂时没有明白是为什么。。
if (key==null) { //如果参数为 null
while (i.hasNext()) { //判断迭代器游标后是否还有值
Entry<K,V> e = i.next(); //步移游标返回下一个值,缓存给 e
if (e.getKey()==null) //判断条目 e 的键是否是 null
return true; //返回 true
}
} else { //如果参数不为 null
while (i.hasNext()) { //判断迭代器游标后是否还有值
Entry<K,V> e = i.next(); //步移游标返回下一个值,缓存给 e
if (key.equals(e.getKey())) //如果条目 e 的键与 key 相等
return true; //返回 true
}
}
return false; //返回 false
}
/**
* {@inheritDoc}
*
* 实现细节
* 这个实现在 entrySet 上迭代查找一个给定键的条目。如果找到一个这样的条目,返回条目的
* 值。如果迭代完成都
* 没有找到这样的条目,返回 null。注意这个实现需要的时间与 map 的大小成线性关系;许多
* 实现类都重写了这个方法。
*/
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; //返回 null
}
// 修改操作
/**
* {@inheritDoc}
*
* 实现细节
* 这个实现总是抛出一个 UnsupportedOperationException
*/
public V put(K key, V value) {
throw new UnsupportedOperationException();
}
/**
* {@inheritDoc}
*
* 实现细节
* 这个实现在 entrySet 上迭代查找一个包含指定键的条目。如果找到这样的条目,它的值将被
* 以 getValue 操作的方式获得,条目将被从数据结构(和提供支持的 map)中使用迭代器的
* remove 操作移除,并且返回缓存的值。
* 如果迭代结束都没有找到对应条目,返回 null。注意这个实现的时间与 map 的长度成线性关
* 系;许多实现类都重写了这个方法。
*
* 注意如果 entrySet 迭代器不支持 remove 方法并且这个 map 包含一个指定 key 的映
* 射,这个实现将会抛出一个 UnsupportedOperationException
*/
public V remove(Object key) {
Iterator<Entry<K,V>> i = entrySet().iterator();
Entry<K,V> correctEntry = null; //将 null 缓存给 correctEntry
if (key==null) { //如果 key 为 null
while (correctEntry==null && i.hasNext()) {//如果当前条目为 null 并且游标后面还有元素
Entry<K,V> e = i.next(); //步移获取游标后面的元素,缓存给 e
if (e.getKey()==null) //如果 e 条目的键为 null
correctEntry = e; //e 赋值给 correctEntry
}
} else { //如果 key 不为 null
while (correctEntry==null && i.hasNext()) {
Entry<K,V> e = i.next();
if (key.equals(e.getKey())) //如果 key 等于 e 条目的键
correctEntry = e;
}
}
V oldValue = null; //缓存 null 给 oldValue
if (correctEntry !=null) { //如果 correctEntry 不为 null
oldValue = correctEntry.getValue(); //correctEntry 的值赋值给 oldVallue
i.remove(); //迭代器调用 remove 方法
}
return oldValue; //返回 oldValue
}
// 扩展操作
/**
* {@inheritDoc}
*
* 实现细节
* 这个实现在 map 的 entrySet 的 collection 上迭代,并且对迭代操作返回的每个条目调
* 用 map 的 put 操作
*
* 注意如果 map 不支持 put 操作并且制定 map 不是空的话实现会抛出一个
* UnsupportedOperationException
*/
public void putAll(Map<? extends K, ? extends V> m) {
for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) //通过 for 循环 m 的 entrySet
put(e.getKey(), e.getValue()); //调用 put 方法
}
/**
* {@inheritDoc}
*
* 实现细节
* 这个实现调用 entrySet().clear()
*
* 注意如果 entrySet 不支持 clear 操作的话这个实现会抛出一个 UnsupportedOperationException
*/
public void clear() {
entrySet().clear();
}
// 视图
/**
* 每一个这样的类属性在第一次这个视图被请求时被初始化来包含一个视图。视图是状态无关的,所以对于每个视图,没有创建多个的理由。
*
* 既然访问这些类属性的时候不需要执行序列化,可以预计到 java.util.Map 视图类使用这些没有不为 final 的类属性(或者任何 this 之外的类属性)。
*
* 秉承这个规则将会使这些类属性的竞争更加温和。
*
* 同时实现类只读取一次类属性也是必要的,就像:
* {@code
* public Set keySet() {
* Set ks = keySet; //读一次
* if (ks == null) {
* ks = new KeySet();
* keySet = ks;
* }
* return ks;
* }
*}
*/
transient Set<K> keySet;
transient Collection<V> values;
/**
* {@inheritDoc}
*
* 实现细节
* 这个方法实现返回一个子 {@link AbstractSet} 列表的 set。子类的迭代器方法返回一个基于这个 map 的
* entrySet 迭代器的“封装对象”。size 代表了当前 map 的 size 方法,contains 方法代表了当前 map 的
* containsKey 方法。
*
* 当前 set 在第一次调用方法的时候被创建,并且在随后的方法调用结果中返回。没有执行序列化,所以多个方法的
* 调用有不返回同一个 set 的轻微可能性。
*/
public Set<K> keySet() {
Set<K> ks = keySet; //keySet 缓存给 ks
if (ks == null) { //如果 ks 为 null
ks = new AbstractSet<K>() { //返回一个 AbstractSet 匿名子类对象
public Iterator<K> iterator() { //重写 iterator 方法
return new Iterator<K>() { //返回一个 Iterator 接口匿名实现类对象
private Iterator<Entry<K,V>> i = entrySet().iterator(); //缓存 entrySet().iterator() 给 i
public boolean hasNext() { //重写 hasNext 方法,判断游标下一个位置是否有条目
return i.hasNext(); //调用 i 的 hasNext 方法
}
public K next() { //重写 next 方法,注意返回的是游标下一个条目的键
return i.next().getKey(); //注意这里没有实现游标的步移,理论上是永远返回的是同一个值,但是由于迭代器其实是调用 entrySet().iterator() 获得的,所以还需要查看 entrySet 的实现
}
public void remove() { //重写 remove 方法
i.remove(); //调用 i 的 remove 方法
}
};
}
/**以下方法都是在 AbstractSet 中没有定义,实现类自己添加的方法**/
public int size() { //重写获取长度方法
return AbstractMap.this.size(); //调用 AbstractMap 外部类的 size() 方法并返回结果
}
public boolean isEmpty() { //重写 isEmpty 方法
return AbstractMap.this.isEmpty(); //调用 AbstractMap 外部类的 isEmpty() 方法并返回结果
}
public void clear() { //重写 clear 方法
AbstractMap.this.clear(); //调用 AbstractMap 外部类的 clear() 方法
}
public boolean contains(Object k) { //重写 contains 方法
return AbstractMap.this.containsKey(k); //调用 AbstractMap 外部类的 containsKey 方法并返回结果
}
};
keySet = ks; //将 ks 赋值给 keySet
}
return ks; //返回 ks
}
/**
* {@inheritDoc}
*
* 实现细节
* 这个方法实现返回一个 {@link AbstractCollection} 子列表的 collection。子类的迭代器方法返回一个
* 基于 map 的 entrySet() 迭代器的“封装对象”。size 方法代表了这个 map 的 size方法,contains 方法
* 代表了这个 map 的 containsValue 方法。注意这里与上面做了区分,上面 keySet 的 conatins 方法代表
* 了这个 map 的 containsKey 方法。
*
* 这个 collection 在第一次调用这个方法时候创建,并且在其后的方法调用中作为结果返回。没有执行序列化,所
* 以多个方法的调用有不返回同一个 collection 的轻微可能性。这里也与 keySet 做了区别,JavaDoc 写的还
* 是很细致的。
*/
public Collection<V> values() {
Collection<V> vals = values; //values 缓存给 valus
if (vals == null) { //如果 vals 为 null
vals = new AbstractCollection<V>() { //返回 AbstractCollection 匿名子类对象
public Iterator<V> iterator() { //返回 Iterator 匿名实现类对象
return new Iterator<V>() {
private Iterator<Entry<K,V>> i = entrySet().iterator(); //将 entrySet().iterator() 缓存给 i
public boolean hasNext() { //重写 hasNext 方法
return i.hasNext(); //调用 i 的 hasNext 方法
}
public V next() {
return i.next().getValue(); //调用 i 的 next() 方法并返回条目的值,这里与 keySet 中匿名迭代器的实现对象做了区分
}
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); //调用 AbstractMap 外部类的 containsValue 方法,并返回结果,这里与 keySet 的 AbstractSet 匿名子类的 contains 方法做了区分
}
};
values = vals; //vals 赋值给 values
}
return vals; //返回 vals
}
/**方法依然是没有实现的,可以预知每一种子类根据自己的特性实现的方式不同**/
public abstract Set<Entry<K,V>> entrySet();
// 比较和哈希
/**
* 比较当前 map 与指定对象的相等性。如果给定对象也是一个 map 并且两个 map 持有全部相同的映射关系则返回
* true。更正式地说,对于两个 map m1 和 m2,如果
* m1.entrySet().equals(m2.entrySet())
* 那么就认为它们是相同的。 这保证了在 Map 接口的不同实现类中 equals 方法都能正常地工作。
*
* 实现细节
* 这个方法实现线检查指定对象是否为当前 map;如果是则返回 true。然后,它检查指定对象是否是一个长度等于
* 当前 map 长度的 map;如果不是,则返回 false。如果是,它将迭代当前 map 的 entrySet collection,
* 并且指定 map 是否包含当前 map 中持有的每一个映射关系。如果指定 map 没有持有类似的任何一个映射,则返
* 回 false。如果迭代完成,则返回 true。
*/
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Map))
return false;
Map<?,?> m = (Map<?,?>) o; //强转 o 缓存给 m,为什么这里的范型是两个 ? 而不是 K,V 没有明白。。
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) { //value 为 null
if (!(m.get(key)==null && m.containsKey(key))) //如果 m.get(key) 不为 null 或者 m 不包含这样的键
return false; //返回 false
} else { //value 不为 null
if (!value.equals(m.get(key))) //如果 value 不等于 m.get(key)
return false; //返回 false
}
}
} catch (ClassCastException unused) {
return false; //不抛出 ClassCastException 异常,返回 false
} catch (NullPointerException unused) {
return false; //不抛出 NullPointerException 异常,返回 false
}
return true; //返回 true
}
/**
* 返回当前 map 的 hash 值。一个 map 的 hash 值是由这个 map 的 entrySet() 视图中的每一个条目的
* hash 值之和累加得到的。这保证了对于任意两个 map m1 和 m2, m1.equals(m2) 隐式的包含了
* m1.hashCode() == m2.hashCode(),就像 {@link Object#hashCode} 普遍规约要求的那样。
*
* 实现细节
* 这个方法实现在 entrySet() 进行迭代,对于 set 中的每一个元素(条目)调用 {@link
* Map.Entry#hashCode hashCode()},然后累加它们的结果。
*/
public int hashCode() {
int h = 0;
Iterator<Entry<K,V>> i = entrySet().iterator();
while (i.hasNext())
h += i.next().hashCode();
return h;
}
/**
* 返回一个代表了当前 map 的字符串。代表字符串包含了一列符合 map 的 entrySet 视图的迭代器顺序的键值映
* 射关系,封闭在一对 "{}" 中。相连的映射关系由字符 ", " (逗号和空格)断开。每个键值映射关系将转为键跟
* 着一个等于符号“=”跟着一个值的形式。就像 {@link String#valueOf(Object)} 定义的,键s和值s被转换成
* 了字符串。
*/
public String toString() {
Iterator<Entry<K,V>> i = entrySet().iterator(); //调用 entrySet().iterator() 方法,返回值缓存给 i
if (! i.hasNext()) //如果 i 中游标后不再有值,就是没有一个值
return "{}"; //返回 "{}"
StringBuilder sb = new StringBuilder(); //使用 StirngBuilder 完成字符串拼接
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()) //直到 i 中游标后不再有值
return sb.append('}').toString(); //拼接尾符并转换成 String 返回
sb.append(',').append(' ');
}
}
/**
* 返回当前 AbstractMap 实例的一个浅拷贝:键s和值s不会被克隆。
*/
protected Object clone() throws CloneNotSupportedException {
AbstractMap<?,?> result = (AbstractMap<?,?>)super.clone();
result.keySet = null;
result.values = null;
return result;
}
/**
* 为 SimpleEntry 和 SimpleImmutableEntry 提供的工具方法。
* 测试相等性,检查 nulls。
*/
private static boolean eq(Object o1, Object o2) {
return o1 == null ? o2 == null : o1.equals(o2);
}
/**
* 实现类注意:SimpleEntry 和 SimpleImmutableEntry 是与类明显不相关的类,就算他们分享了相同的代
* 码。既然你不能加上或者减去一个子类中的最终类属性,它们不能分享代表性,并且重复的代码太少了,无法保证公
* 开一个公共的抽象类。
**/
/**
* 一个维护一个键和一个值的条目。值可能被使用 setValue 方法改变。这个类促进了建立一个自定义 map 实现类
* 的过程。比如,它能通过 Map.entrySet().toArray 方法更方便的返回 SimpleEntry 实例的数组。
*
* SimpleEntry 内部静态类,实现了 Entry 接口,与 Serializable 接口
*/
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(K key, V value) {
this.key = key;
this.value = 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;
}
/**
* 比较指定对象和当前条目的相等性。如果给定对象也是一个 map 条目并且两个条目持有相同的映射关系,则
* 返回 true。更正式地说,两个条目 e1 和 e2 如果存在
* if
* (e1.getKey()==null ?
* e2.getKey()==null :
* e1.getKey().equals(e2.getKey()))
* &&
* (e1.getValue()==null ?
* e2.getValue()==null :
* e1.getValue().equals(e2.getValue()))
* 这样的相同映射关系,则返回 true。这保证了 {@code Map.Entry} 接口的不同实现类可以正常地使用
* {@code 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()); //注意这里使用工具方法进行比较
}
/**
* 返回当前 map 条目的 hash 值。一个 map 条目 {@code e} 的 hash 值被定义为:
* (e.getKey()==null ? 0 : e.getKey().hashCode()) ^
* (e.getValue()==null ? 0 : e.getValue().hashCode())
* 这保证了对于两个条目 {@code e1} 和 {@code e2}, {@code e1.equals(e2)} 隐式的包含了
* {@code e1.hashCode()==e2.hashCode()},就像 {@link Object#hashCode} 的普遍规约要求的那
* 样。
*/
public int hashCode() {
return (key == null ? 0 : key.hashCode()) ^
(value == null ? 0 : value.hashCode());
}
/**
* 返回一个代表 map 条目的字符串。
*/
public String toString() {
return key + "=" + value;
}
}
/**
* 一个维护不可变的键和值的条目。这个类不支持 setValue 方法。这个类可以方便的返回线程安全的键值映射快照
* 方法中使用。
*
* SimpleImmutableEntry 类,实现了 Entry 接口与 Serializable 接口
*/
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,因为这个类要实现一个 immutable map 条目。
*/
public V setValue(V value) {
throw new UnsupportedOperationException();
}
/**
* 比较指定对象与当前条目的相等性。如果给定对象也是一个 map 条目并且两个条目代表的映射关系相同,则
* 返回 true。更正式地说,对于两个条目 e1 和 e2,只要存在:
* (e1.getKey()==null ?
* e2.getKey()==null :
* e1.getKey().equals(e2.getKey()))
* &&
* (e1.getValue()==null ?
* e2.getValue()==null :
* e1.getValue().equals(e2.getValue()))
* 的关系就返回 true。这保证了在 {@code Map.Entry} 接口的不同实现类中 {@code 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()); //注意这里调用的是工具方法
}
/**
* 返回当前 map 条目的 hash 值。一个 map 条目 e 的 hash 值被定义为:
* (e.getKey()==null ? 0 : e.getKey().hashCode()) ^
* (e.getValue()==null ? 0 : e.getValue().hashCode())
* 这保证了对于任意两个条目 e1 和 e2, {@code e1.equals(e2)} 隐含了
* {@code e1.hashCode()==e2.hashCode()},就像 {@link Object#hashCode} 普遍规约要求的那
* 样。
*/
public int hashCode() {
return (key == null ? 0 : key.hashCode()) ^
(value == null ? 0 : value.hashCode());
}
/**
* 返回代表了当前 map 条目的字符串。
*/
public String toString() {
return key + "=" + value;
}
}
}
通过 AbstractMap 的源码可以看到
1、首先对于 put 方法,AbstractMap 是没有实现的,需要由最终实现类自己实现。
2、可以看到除了 map 本身的存在性以及 map 的长度有关的方法外,所有方法都是在操作 entrySet() 或者 entrySet().iterator(),可以确定所谓操作 map,核心其实就是操作 entrySet。而 entrySet 方法在 AbstractMap 中还没有实现,需要由最终实现类自己实现。
3、AbstractMap 提供了两个方便的条目操作静态内部类与一个辅助这两个类的静态工具方法,其中有一个通过实现了对象的不可变特性带有线程安全特性。通过这两个类的构造方法可以方便快速的建立一个条目。
以上就是对于 Map 数据结构中 Map 接口与 AbstractMap 抽象实现了的介绍与分析,下一篇中将对 HashMap 进行介绍与分析。