Java8 Map接口新特性示例源码分析
新特性
在Java 8中,java.util.Map
接口引入了一些新的功能和方法来增强对映射数据的操作。下面是Java 8中Map
的主要变化:
-
默认方法(Default Methods):
Map
接口引入了多个默认方法,包括getOrDefault
、forEach
、putIfAbsent
、remove
、replace
、computeIfAbsent
、computeIfPresent
、compute
、replaceAll
和merge
等方法。这些默认方法提供了更方便的操作方式,并且允许在不破坏现有代码的情况下向Map
接口添加新的功能。 -
forEach
方法:新增的forEach
方法允许使用lambda表达式或方法引用遍历Map
中的键值对,并执行指定的操作。 -
computeIfAbsent
和computeIfPresent
方法:这两个方法分别用于根据键是否存在进行计算操作。computeIfAbsent
方法在指定键不存在时计算一个值并将其与键关联;computeIfPresent
方法在指定键存在时重新计算一个值并将其与键关联。 -
merge
方法:merge
方法用于将指定键值对合并到Map
中,根据旧值和新值进行自定义操作。它提供了一种简洁而灵活的方式来处理合并操作。 -
replaceAll
方法:replaceAll
方法允许使用指定的函数对Map
中的每个键值对进行替换操作。它可以方便地一次性替换所有的值。
这些变化使得在Java 8中使用Map
更加方便和灵活,同时提供了更多的功能和操作方式。开发人员可以根据具体需求选择适合的方法来操作Map
对象,并利用默认方法和新的功能来简化代码并提高效率。
方法示例
以下是Java 8中java.util.Map
接口新增的方法及其用法总结,同时提供了示例代码,并输出结果值:
default V getOrDefault(Object key, V defaultValue)
获取指定键对应的值,如果键不存在则返回默认值。
示例代码:
Map<String, Integer> map = new HashMap<>();
map.put("A", 10);
int value = map.getOrDefault("B", 0); // 返回默认值 0,因为键 "B" 不存在
System.out.println(value); // 输出:0
default void forEach(BiConsumer<? super K, ? super V> action)
对Map
中的每个键值对执行指定操作。
示例代码:
Map<String, Integer> map = new HashMap<>();
map.put("A", 10);
map.put("B", 20);
map.forEach((key, value) -> System.out.println(key + ": " + value));
// 输出:
// A: 10
// B: 20
default V putIfAbsent(K key, V value)
当指定键不存在时,将键值对添加到Map
中。
示例代码:
Map<String, Integer> map = new HashMap<>();
map.put("A", 10);
map.putIfAbsent("B", 20); // 添加键值对 "B"->20
System.out.println(map.get("B")); // 输出:20
default boolean remove(Object key, Object value)
仅当指定键与给定的值相匹配时,从Map
中移除键值对。
示例代码:
Map<String, Integer> map = new HashMap<>();
map.put("A", 10);
map.remove("A", 20); // 不匹配,不执行删除操作
map.remove("A", 10); // 匹配,移除键 "A"
System.out.println(map.containsKey("A")); // 输出:false
default boolean replace(K key, V oldValue, V newValue)
仅当指定键与给定的旧值相匹配时,替换对应的值。
示例代码:
Map<String, Integer> map = new HashMap<>();
map.put("A", 10);
map.replace("A", 20, 30); // 不匹配,不执行替换操作
map.replace("A", 10, 30); // 匹配,将值从 10 替换为 30
System.out.println(map.get("A")); // 输出:30
default V replace(K key, V value)
替换指定键的值,并返回旧值。
示例代码:
Map<String, Integer> map = new HashMap<>();
map.put("A", 10);
int oldValue = map.replace("A", 20); // 将键 "A" 的值从 10 替换为 20,并返回旧值 10
System.out.println(oldValue); // 输出:10
default V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)
如果指定键不存在,则根据指定函数计算一个值,并将其与键关联。
示例代码:
Map<String, Integer> map = new HashMap<>();
map.put("A", 10);
int newValue = map.computeIfAbsent("B", key -> key.length()); // 计算键 "B" 的长度作为新值
System.out.println(newValue); // 输出:1
default V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)
如果指定键存在,则根据指定函数重新计算一个值,并将其与键关联。
示例代码:
Map<String, Integer> map = new HashMap<>();
map.put("A", 10);
map.computeIfPresent("A", (key, value) -> value * 2); // 根据旧值计算新值
System.out.println(map.get("A")); // 输出:20
default V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)
根据指定函数重新计算一个值,并将其与键关联。
示例代码:
Map<String, Integer> map = new HashMap<>();
map.put("A", 10);
map.compute("A", (key, value) -> value * 2); // 根据旧值计算新值
System.out.println(map.get("A")); // 输出:20
default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function)
使用指定函数对Map
中的每个键值对进行替换操作。
示例代码:
Map<String, Integer> map = new HashMap<>();
map.put("A", 10);
map.put("B", 20);
map.replaceAll((key, value) -> value * 2); // 将每个值乘以 2
System.out.println(map.get("A")); // 输出:20
System.out.println(map.get("B")); // 输出:40
default V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction)
将指定的键值对合并到Map
中,根据旧值和新值进行自定义操作。
示例代码:
Map<String, Integer> map = new HashMap<>();
map.put("A", 10);
map.merge("A", 5, Integer::sum); // 将旧值和新值相加
System.out.println(map.get("A")); // 输出:15
这些方法提供了更丰富和便捷的操作Map
对象的功能,包括处理默认值、批量操作、视图操作和替代操作等。开发人员可以根据具体需求选择适合的方法来操作Map
对象,并根据示例代码输出结果值来验证操作的效果。
中文源码
/**
* 一个将键映射到值的对象。地图不能包含重复的键;每个键最多可以映射到一个值。
*
* <p>此接口取代了<tt>Dictionary</tt>类,它是一个完全抽象的类而不是一个接口。
*
* <p><tt>Map</tt>接口提供了三种<i>集合视图</i>,允许将地图的内容视为键的集合、值的集合或键值映射的集合。
* 地图的<i>顺序</i>被定义为迭代器在地图的集合视图上返回其元素的顺序。一些地图实现(如<tt>TreeMap</tt>类)对其顺序有明确保证,
* 而其他地图实现(如<tt>HashMap</tt>类)则没有这样的保证。
*
* <p>注意:如果使用可变对象作为地图的键,必须非常小心。当对象作为键存在于地图中时,如果以一种影响到<tt>equals</tt>比较的方式改变了对象的值,
* 则地图的行为是未指定的。特别禁止地图包含自身作为键。虽然允许地图包含自身作为值,但是需要极度谨慎:这种情况下<tt>equals</tt>和<tt>hashCode</tt>方法的行为不再有明确定义。
*
* <p>所有通用的地图实现类应该提供两个“标准”的构造函数:一个无参构造函数,创建一个空地图;一个带有单个<tt>Map</tt>类型参数的构造函数,
* 创建一个与其参数具有相同键值映射的新地图。实际上,后一个构造函数允许用户复制任何地图,生成一个等效的目标类的地图。没有办法强制执行此建议(因为接口不能包含构造函数),
* 但是JDK中的所有通用地图实现都遵守了这一规定。
*
* <p>此接口中包含的“破坏性”方法,即修改它们所操作的地图的方法,在不支持该操作的地图上指定抛出
* <tt>UnsupportedOperationException</tt>。如果是这种情况,这些方法可以但不必抛出
* <tt>UnsupportedOperationException</tt>,如果调用对地图没有影响,也可以抛出异常。
* 例如,在不可修改的地图上调用{@link #putAll(Map)}方法可能会抛出异常,如果要“叠加”的映射为空,则可能会抛出异常。
*
* <p>某些地图实现对它们可以包含的键和值有限制。例如,某些实现禁止空键和值,而某些实现对其键的类型有限制。
* 尝试插入不符合条件的键或值会抛出未经检查的异常,通常是<tt>NullPointerException</tt>或<tt>ClassCastException</tt>。
* 尝试查询不符合条件的键或值的存在可能会抛出异常,或者它可能只返回false;某些实现将表现出前一种行为,而其他实现将表现出后一种行为。
* 更一般地说,对不符合条件的键或值执行操作,其完成不会导致不符合要素插入到地图中,可能会抛出异常,也可能成功,
* 这取决于实现的选择。这样的异常在此接口的规范中被标记为“可选”。
*
* <p>许多集合框架接口中的方法都是以{@link Object#equals(Object) equals}方法定义的。
* 例如,{@link #containsKey(Object) containsKey(Object key)}方法的规范表示:
* "当且仅当此地图包含一个映射到键<tt>k</tt>的键<tt>k</tt>时,返回<tt>true</tt>,
* 其中<tt>(key==null ? k==null : key.equals(k))</tt>。"不能将此规范误解为意味着调用
* <tt>Map.containsKey</tt>时,对于任何键<tt>k</tt>,都会调用<tt>key.equals(k)</tt>。
* 实现可以根据需要自由实现优化,避免调用<tt>equals</tt>,例如,通过首先比较两个键的哈希码。({@link Object#hashCode()}规范保证具有不相等哈希码的两个对象不相等。)
* 更一般地说,在各种集合框架接口的实现中,如果实现者认为合适,可以利用底层{@link Object}方法的指定行为。
*
* <p>某些地图操作执行对地图的递归遍历,可能在直接或间接包含自身的地图上抛出异常。
* 这包括{@code clone()}、{@code equals()}、{@code hashCode()}和{@code toString()}方法。
* 实现可以选择处理自引用的情况,但是目前大多数实现没有这样做。
*
* <p>此接口是<a href="{@docRoot}/../technotes/guides/collections/index.html">Java Collections Framework</a>的成员。
*
* @param <K> 此地图中维护的键的类型
* @param <V> 映射值的类型
*
* @author Josh Bloch
* @see HashMap
* @see TreeMap
* @see Hashtable
* @see SortedMap
* @see Collection
* @see Set
* @since 1.2
*/
public interface Map<K,V> {
// 查询操作
/**
* 返回此地图中键值映射的数量。如果地图包含超过<tt>Integer.MAX_VALUE</tt>个元素,则返回<tt>Integer.MAX_VALUE</tt>。
*
* @return 此地图中键值映射的数量
*/
int size();
/**
* 如果此地图不包含键值映射,则返回<tt>true</tt>。
*
* @return 如果此地图不包含键值映射,则返回<tt>true</tt>
*/
boolean isEmpty();
/**
* 如果此地图包含指定键的映射,则返回<tt>true</tt>。
* 更正式地说,当且仅当此地图包含一个为键<tt>k</tt>映射的键<tt>k</tt>时,返回<tt>true</tt>,
* 其中<tt>(key==null ? k==null : key.equals(k))</tt>。(最多可以有一个这样的映射。)
*
* @param key 要在此地图中测试其存在性的键
* @return 如果此地图包含指定键的映射,则返回<tt>true</tt>
* @throws ClassCastException 如果键对于此地图来说是不合适的类型
* (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
* @throws NullPointerException 如果指定的键为null,并且此地图不允许空键
* (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
*/
boolean containsKey(Object key);
/**
* 如果此地图将一个或多个键映射到指定值,则返回<tt>true</tt>。
* 更正式地说,当且仅当此地图包含至少一个映射到值<tt>v</tt>的键<tt>k</tt>,
* 使得<tt>(value==null ? v==null : value.equals(v))</tt>时,返回<tt>true</tt>。
* 对于大多数<tt>Map</tt>接口的实现,这个操作可能需要与地图大小成线性关系的时间。
*
* @param value 要在此地图中测试其存在性的值
* @return 如果此地图将一个或多个键映射到指定值,则返回<tt>true</tt>
* @throws ClassCastException 如果该值的类型对于此地图来说是不合适的
* (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
* @throws NullPointerException 如果指定的值为null,并且此地图不允许空值
* (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
*/
boolean containsValue(Object value);
/**
* 返回指定键映射的值,如果此地图不包含键的映射,则返回{@code null}。
*
* <p>更正式地说,如果此地图包含从键{@code k}映射到值{@code v}的映射,
* 使得{@code (key==null ? k==null : key.equals(k))},则此方法返回{@code v};
* 否则返回{@code null}。(最多可以有一个这样的映射。)
*
* <p>如果此地图允许空值,则返回{@code null}的返回值不一定表示该地图不包含键的映射;
* 这也可能意味着地图明确将键映射到{@code null}。
* 可以使用{@link #containsKey containsKey}操作来区分这两种情况。
*
* @param key 要返回其关联值的键
* @return 指定键映射的值,如果此地图不包含键的映射,则返回{@code null}
* @throws ClassCastException 如果该键对于此地图来说是不合适的类型
* (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
* @throws NullPointerException 如果指定的键为null,并且此地图不允许空键
* (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
*/
V get(Object key);
// 修改操作
/**
* 将指定的值与指定的键在此地图中关联(可选操作)。
* 如果地图先前包含了键的映射,那么旧值将被指定的值替换。(如果地图
* <tt>m</tt>包含键<tt>k</tt>的映射,则称地图包含键<tt>k</tt>)。
*
* @param key 要与指定值关联的键
* @param value 要与指定键关联的值
* @return 与<tt>key</tt>关联的先前值,如果对于<tt>key</tt>没有映射关系,则返回<tt>null</tt>。
* (如果实现支持<tt>null</tt>值,则<tt>null</tt>返回值也可能表示该地图以前将<tt>null</tt>与<tt>key</tt>关联。)
* @throws UnsupportedOperationException 如果此地图不支持<tt>put</tt>操作
* @throws ClassCastException 如果指定的键或值的类阻止它们存储在此地图中
* @throws NullPointerException 如果指定的键或值为null,并且此地图不允许空键或值
* @throws IllegalArgumentException 如果指定的键或值的某些属性阻止它们存储在此地图中
*/
V put(K key, V value);
/**
* 如果存在,则从此地图中删除一个键的映射(可选操作)。
* 更正式地说,如果地图包含键<tt>k</tt>到值<tt>v</tt>的映射,
* 使得<code>(key==null ? k==null : key.equals(k))</code>,
* 那么将删除该映射。(地图最多可以包含一个这样的映射。)
*
* <p>返回此地图先前关联键的值,如果地图不包含键的映射,则返回<tt>null</tt>。
*
* <p>如果此地图允许空值,则返回<tt>null</tt>的返回值不一定表示该地图不包含键的映射;
* 这也可能意味着地图明确将键映射到<tt>null</tt>。
*
* <p>一旦调用返回,地图将不再包含指定键的映射。
*
* @param key 要从地图中删除其映射的键
* @return 与<tt>key</tt>先前关联的值,如果对于<tt>key</tt>没有映射关系,则返回<tt>null</tt>
* @throws UnsupportedOperationException 如果此地图不支持<tt>remove</tt>操作
* @throws ClassCastException 如果该键对于此地图来说是不合适的类型
* (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
* @throws NullPointerException 如果指定的键为null,并且此地图不允许空键
* (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
*/
V remove(Object key);
// 批量操作
/**
* 将指定地图中的所有映射复制到此地图中(可选操作)。
* 此调用的效果等同于在指定地图中的每个映射上调用{@link #put(Object,Object) put(k, v)}。
* 如果在操作正在进行时修改了指定地图,则该操作的行为是未定义的。
*
* @param m 要存储在此地图中的映射
* @throws UnsupportedOperationException 如果此地图不支持<tt>putAll</tt>操作
* @throws ClassCastException 如果指定地图中的键或值的类阻止它们存储在此地图中
* @throws NullPointerException 如果指定地图为null,或者如果此地图不允许空键或值,并且指定地图包含空键或值
* @throws IllegalArgumentException 如果指定地图中的某个键或值的某些属性阻止它们存储在此地图中
*/
void putAll(Map<? extends K, ? extends V> m);
/**
* 从此地图中删除所有映射关系(可选操作)。
* 此调用返回后,地图将为空。
*
* @throws UnsupportedOperationException 如果此地图不支持<tt>clear</tt>操作
*/
void clear();
// 视图
/**
* 返回此地图中包含的键的{@link Set}视图。
* 该集合由地图支持,因此对地图的更改会反映在集合中,反之亦然。
* 如果在迭代集合时修改了地图(通过迭代器自己的<tt>remove</tt>操作除外),
* 迭代的结果是不确定的。该集合支持元素删除,
* 可以通过<tt>Iterator.remove</tt>、<tt>Set.remove</tt>、
* <tt>removeAll</tt>、<tt>retainAll</tt>和<tt>clear</tt>操作从地图中删除相应的映射。
* 它不支持<tt>add</tt>或<tt>addAll</tt>操作。
*
* @return 此地图中包含的键的{@link Set}视图
*/
Set<K> keySet();
/**
* 返回此地图中包含的值的{@link Collection}视图。
* 该集合由地图支持,因此对地图的更改会反映在集合中,反之亦然。
* 如果在迭代集合时修改了地图(通过迭代器自己的<tt>remove</tt>操作除外),
* 迭代的结果是不确定的。该集合支持元素删除,
* 可以通过<tt>Iterator.remove</tt>、<tt>Collection.remove</tt>、
* <tt>removeAll</tt>、<tt>retainAll</tt>和<tt>clear</tt>操作从地图中删除相应的映射。
* 它不支持<tt>add</tt>或<tt>addAll</tt>操作。
*
* @return 此地图中包含的值的{@link Collection}视图
*/
Collection<V> values();
/**
* 返回此地图中包含的映射的{@link Set}视图。
* 该集合由地图支持,因此对地图的更改会反映在集合中,反之亦然。
* 如果在迭代集合时修改了地图(通过迭代器自己的<tt>remove</tt>操作除外),
* 迭代的结果是不确定的。该集合支持元素删除,
* 可以通过<tt>Iterator.remove</tt>、<tt>Set.remove</tt>、
* <tt>removeAll</tt>、<tt>retainAll</tt>和<tt>clear</tt>操作从地图中删除相应的映射。
* 它不支持<tt>add</tt>或<tt>addAll</tt>操作。
*
* @return 此地图中包含的映射的{@link Set}视图
*/
Set<Map.Entry<K, V>> entrySet();
/**
* 一个映射条目(键-值对)。Map.entrySet方法返回一个包含此类元素的映射视图。获取映射条目的唯一方法是通过此集合视图的迭代器。这些Map.Entry对象仅在迭代期间有效;更正式地说,如果在迭代器返回条目之后修改了支持映射,则映射条目的行为是未定义的,除非通过映射条目上的setValue操作。
*
* @see Map#entrySet()
* @since 1.2
*/
interface Entry<K,V> {
/**
* 返回与此条目对应的键。
*
* @return 与此条目对应的键
* @throws IllegalStateException 如果条目已从支持映射中删除,则实现可以(但不是必须)抛出此异常。
*/
K getKey();
/**
* 返回与此条目对应的值。如果该映射已从支持映射中删除(通过迭代器的remove操作),则对此调用的结果是未定义的。
*
* @return 与此条目对应的值
* @throws IllegalStateException 如果条目已从支持映射中删除,则实现可以(但不是必须)抛出此异常。
*/
V getValue();
/**
* 使用指定的值替换与此条目对应的值(可选操作)。 (写入到映射中。)如果该映射已从映射中删除(通过迭代器的remove操作),则此调用的行为是未定义的。
*
* @param value 要存储在此条目中的新值
* @return 条目对应的旧值
* @throws UnsupportedOperationException 如果不支持后台映射的put操作
* @throws ClassCastException 如果指定值的类阻止将其存储在后台映射中
* @throws NullPointerException 如果后台映射不允许空值,并且指定值为空
* @throws IllegalArgumentException 如果该值的某些属性阻止将其存储在后台映射中
* @throws IllegalStateException 如果条目已从支持映射中删除,则实现可以(但不是必须)抛出此异常。
*/
V setValue(V value);
/**
* 将指定对象与此条目进行比较,判断是否相等。如果给定对象也是一个映射条目,并且两个条目表示相同的映射,则返回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()))
* 这确保了equals方法在Map.Entry接口的不同实现之间正常工作。
*
* @param o 要与此映射条目进行比较的对象
* @return 如果指定对象等于此映射条目,则返回true
*/
boolean equals(Object o);
/**
* 返回此映射条目的哈希码值。映射条目e的哈希码定义如下:<pre>
* (e.getKey()==null ? 0 : e.getKey().hashCode()) ^
* (e.getValue()==null ? 0 : e.getValue().hashCode())
* </pre>
* 这确保了对于任何两个条目e1和e2,如果e1.equals(e2),则e1.hashCode()==e2.hashCode(),这是Object.hashCode的通用约定所要求的。
*
* @return 此映射条目的哈希码值
* @see Object#hashCode()
* @see Object#equals(Object)
* @see #equals(Object)
*/
int hashCode();
/**
* 返回一个按键的自然顺序比较Map.Entry的比较器。
*
* <p>返回的比较器是可序列化的,并且在比较具有null键的条目时抛出NullPointerException。
*
* @param <K> map键的Comparable类型
* @param <V> map值的类型
* @return 一个按键的自然顺序比较Map.Entry的比较器
* @see Comparable
* @since 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());
}
/**
* 返回一个按值的自然顺序比较Map.Entry的比较器。
*
* <p>返回的比较器是可序列化的,并且在比较具有null值的条目时抛出NullPointerException。
*
* @param <K> map键的类型
* @param <V> map值的Comparable类型
* @return 一个按值的自然顺序比较Map.Entry的比较器
* @see Comparable
* @since 1.8
*/
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());
}
/**
* 返回一个按键使用给定Comparator进行比较的Map.Entry的比较器。
*
* <p>如果指定的比较器也是可序列化的,则返回的比较器也将是可序列化的。
*
* @param <K> map键的类型
* @param <V> map值的类型
* @param cmp 键的比较器
* @return 一个按键进行比较的Map.Entry的比较器
* @since 1.8
*/
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());
}
/**
* 返回一个按值使用给定Comparator进行比较的Map.Entry的比较器。
*
* <p>如果指定的比较器也是可序列化的,则返回的比较器也将是可序列化的。
*
* @param <K> map键的类型
* @param <V> map值的类型
* @param cmp 值的比较器
* @return 一个按值进行比较的Map.Entry的比较器
* @since 1.8
*/
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());
}
}
// 比较和哈希
/**
* 将指定对象与此映射进行比较,判断是否相等。如果给定对象也是一个映射,并且两个映射表示相同的映射,则返回true。更正式地说,当且仅当以下条件满足时,两个映射m1和m2表示相同的映射:
* m1.entrySet().equals(m2.entrySet())。这确保了equals方法在Map接口的不同实现之间正常工作。
*
* @param o 要与此映射进行比较的对象
* @return 如果指定对象等于此映射,则返回true
*/
boolean equals(Object o);
/**
* 返回此映射的哈希码值。映射的哈希码定义为映射的entrySet()视图中每个条目的哈希码之和。这确保了对于任何两个映射m1和m2,如果m1.equals(m2),则m1.hashCode()==m2.hashCode(),这是Object.hashCode的通用约定所要求的。
*
* @return 此映射的哈希码值
* @see Map.Entry#hashCode()
* @see Object#equals(Object)
* @see #equals(Object)
*/
int hashCode();
// 默认方法
/**
* 返回与指定键关联的值,如果该映射不包含键的映射,则返回defaultValue。
*
* @implSpec
* 默认实现对此方法的同步或原子性属性不提供任何保证。任何提供原子性保证的实现都必须覆盖此方法并记录其并发属性。
*
* @param key 要返回其关联值的键
* @param defaultValue 键的默认映射
* @return 与指定键关联的值,如果该映射不包含键的映射,则返回defaultValue
* @throws ClassCastException 如果键的类型对于此映射来说不合适
* (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
* @throws NullPointerException 如果指定键为null且此映射不允许null键
* (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
* @since 1.8
*/
default V getOrDefault(Object key, V defaultValue) {
V v;
return (((v = get(key)) != null) || containsKey(key))
? v
: defaultValue;
}
// 比较和哈希
/**
* 将指定对象与此映射进行比较,判断是否相等。如果给定对象也是一个映射,并且两个映射表示相同的映射,则返回true。更正式地说,当且仅当以下条件满足时,两个映射m1和m2表示相同的映射:
* m1.entrySet().equals(m2.entrySet())。这确保了equals方法在Map接口的不同实现之间正常工作。
*
* @param o 要与此映射进行比较的对象
* @return 如果指定对象等于此映射,则返回true
*/
boolean equals(Object o);
/**
* 返回此映射的哈希码值。映射的哈希码定义为映射的entrySet()视图中每个条目的哈希码之和。这确保了对于任何两个映射m1和m2,如果m1.equals(m2),则m1.hashCode()==m2.hashCode(),这是Object.hashCode的通用约定所要求的。
*
* @return 此映射的哈希码值
* @see Map.Entry#hashCode()
* @see Object#equals(Object)
* @see #equals(Object)
*/
int hashCode();
// 默认方法
/**
* 返回与指定键关联的值,如果该映射不包含键的映射,则返回defaultValue。
*
* @implSpec
* 默认实现对此方法的同步或原子性属性不提供任何保证。任何提供原子性保证的实现都必须覆盖此方法并记录其并发属性。
*
* @param key 要返回其关联值的键
* @param defaultValue 键的默认映射
* @return 与指定键关联的值,如果该映射不包含键的映射,则返回defaultValue
* @throws ClassCastException 如果键的类型对于此映射来说不合适
* (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
* @throws NullPointerException 如果指定键为null且此映射不允许null键
* (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
* @since 1.8
*/
default V getOrDefault(Object key, V defaultValue) {
V v;
return (((v = get(key)) != null) || containsKey(key))
? v
: defaultValue;
}
/**
* 对此映射中的每个条目执行给定操作,直到处理完所有条目或操作抛出异常。除非实现类另有规定,否则操作按照entry set迭代的顺序执行(如果指定了迭代顺序)。操作抛出的异常会传递给调用者。
*
* @implSpec
* 默认实现相当于对于此map:
* <pre> {@code
* for (Map.Entry<K, V> entry : map.entrySet())
* action.accept(entry.getKey(), entry.getValue());
* }</pre>
*
* 默认实现对此方法的同步或原子性属性不提供任何保证。任何提供原子性保证的实现都必须覆盖此方法并记录其并发属性。
*
* @param action 要对每个条目执行的操作
* @throws NullPointerException 如果指定操作为null
* @throws ConcurrentModificationException 如果在迭代期间发现已删除条目
* @since 1.8
*/
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) {
// 这通常意味着条目不再存在于映射中。
throw new ConcurrentModificationException(ise);
}
action.accept(k, v);
}
}
/**
* 使用给定函数对每个条目的值进行替换,直到处理完所有条目或函数抛出异常。函数抛出的异常会传递给调用者。
*
* @implSpec
* 默认实现相当于对于此map:
* <pre> {@code
* for (Map.Entry<K, V> entry : map.entrySet())
* entry.setValue(function.apply(entry.getKey(), entry.getValue()));
* }</pre>
*
* 默认实现对此方法的同步或原子性属性不提供任何保证。任何提供原子性保证的实现都必须覆盖此方法并记录其并发属性。
*
* @param function 要对每个条目应用的函数
* @throws UnsupportedOperationException 如果此映射的entry set迭代器不支持set操作
* @throws ClassCastException 如果替换值的类阻止将其存储在此映射中
* @throws NullPointerException 如果指定函数为null,或者指定替换值为null,并且此映射不允许null值
* @throws ClassCastException 如果替换值的类不适合此映射
* (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
* @throws NullPointerException 如果函数或替换值为null,并且此映射不允许null键或值
* (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
* @throws IllegalArgumentException 如果替换值的某些属性阻止将其存储在此映射中
* (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
* @throws ConcurrentModificationException 如果在迭代期间发现已删除条目
* @since 1.8
*/
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) {
// 这通常意味着条目不再存在于映射中。
throw new ConcurrentModificationException(ise);
}
// 函数引发的ise不是并发修改异常。
v = function.apply(k, v);
try {
entry.setValue(v);
} catch(IllegalStateException ise) {
// 这通常意味着条目不再存在于映射中。
throw new ConcurrentModificationException(ise);
}
}
}
/**
* 如果指定键尚未与值关联(或映射到null),则将其与给定值关联,并返回null;否则返回当前值。
*
* @implSpec
* 默认实现对于此map相当于:
*
* <pre> {@code
* V v = map.get(key);
* if (v == null)
* v = map.put(key, value);
*
* return v;
* }</pre>
*
* 默认实现对此方法的同步或原子性属性不提供任何保证。任何提供原子性保证的实现都必须覆盖此方法并记录其并发属性。
*
* @param key 要与指定值关联的键
* @param value 与指定键关联的值
* @return 与指定键关联的先前值,如果该键没有映射,则为null。
* (如果实现支持null值,则此返回值也可能表示先前将null与键关联。)
* @throws UnsupportedOperationException 如果此映射不支持put操作
* @throws ClassCastException 如果键或值的类不适合此映射
* @throws NullPointerException 如果指定键或值为null,并且此映射不允许null键或值
* @throws IllegalArgumentException 如果指定键或值的某些属性阻止将其存储在此映射中
* @since 1.8
*/
default V putIfAbsent(K key, V value) {
V v = get(key);
if (v == null) {
v = put(key, value);
}
return v;
}
/**
* 仅当指定的键当前映射到指定的值时,才删除该键的条目。
*
* @implSpec
* 默认实现对于此map相当于:
*
* <pre> {@code
* if (map.containsKey(key) && Objects.equals(map.get(key), value)) {
* map.remove(key);
* return true;
* } else
* return false;
* }</pre>
*
* 默认实现对此方法的同步或原子性属性不提供任何保证。任何提供原子性保证的实现都必须覆盖此方法并记录其并发属性。
*
* @param key 与指定值关联的键
* @param value 预期与指定键关联的值
* @return 如果值被删除,则返回true
* @throws UnsupportedOperationException 如果此映射不支持remove操作
* (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
* @throws ClassCastException 如果键或值的类对于此映射来说不合适
* (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
* @throws NullPointerException 如果指定键或值为null,并且此映射不允许null键或值
* (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
* @since 1.8
*/
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;
}
/**
* 仅当指定键当前映射到指定值时,才替换该键的条目。
*
* @implSpec
* 默认实现对于此map相当于:
*
* <pre> {@code
* if (map.containsKey(key) && Objects.equals(map.get(key), value)) {
* map.put(key, newValue);
* return true;
* } else
* return false;
* }</pre>
*
* 默认实现对此方法的同步或原子性属性不提供任何保证。任何提供原子性保证的实现都必须覆盖此方法并记录其并发属性。
*
* @param key 与指定值关联的键
* @param oldValue 预期与指定键关联的值
* @param newValue 要与指定键关联的值
* @return 如果值被替换,则返回true
* @throws UnsupportedOperationException 如果此映射不支持put操作
* (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
* @throws ClassCastException 如果指定键或值的类阻止将其存储在此映射中
* @throws NullPointerException 如果指定键或newValue为null,并且此映射不允许null键或值
* @throws NullPointerException 如果oldValue为null,并且此映射不允许null值
* (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
* @throws IllegalArgumentException 如果指定键或值的某些属性阻止将其存储在此映射中
* @since 1.8
*/
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;
}
/**
* 仅当指定键当前映射到某个值时,才替换该键的条目。
*
* @implSpec
* 默认实现对于此map相当于:
*
* <pre> {@code
* if (map.containsKey(key)) {
* return map.put(key, value);
* } else
* return null;
* }</pre>
*
* 默认实现对此方法的同步或原子性属性不提供任何保证。任何提供原子性保证的实现都必须覆盖此方法并记录其并发属性。
*
* @param key 与指定值关联的键
* @param value 要与指定键关联的值
* @return 与指定键关联的先前值,或者如果键没有映射,则为null。
* (如果实现支持null值,则此返回值也可能表示先前将null与键关联。)
* @throws UnsupportedOperationException 如果此映射不支持put操作
* (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
* @throws ClassCastException 如果指定键或值的类阻止将其存储在此映射中
* (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
* @throws NullPointerException 如果指定键或值为null,并且此映射不允许null键或值
* @throws IllegalArgumentException 如果指定键或值的某些属性阻止将其存储在此映射中
* @since 1.8
*/
default V replace(K key, V value) {
V curValue;
if (((curValue = get(key)) != null) || containsKey(key)) {
curValue = put(key, value);
}
return curValue;
}
/**
* 如果指定键尚未与值关联(或映射到null),则尝试使用给定的映射函数计算其值并将其输入到此映射,除非为null。
*
* <p>如果函数返回null,则不记录映射。如果函数本身抛出(未检查的)异常,则重新抛出异常,并且不记录映射。最常见的用法是构造一个作为初始映射值或记忆化结果的新对象,例如:
*
* <pre> {@code
* map.computeIfAbsent(key, k -> new Value(f(k)));
* }</pre>
*
* 或者实现一个多值映射{@code Map<K,Collection<V>>},支持每个键的多个值:
*
* <pre> {@code
* map.computeIfAbsent(key, k -> new HashSet<V>()).add(v);
* }</pre>
*
*
* @implSpec
* 默认实现对于此map相当于以下步骤,然后返回当前值或null(如果不存在):
*
* <pre> {@code
* if (map.get(key) == null) {
* V newValue = mappingFunction.apply(key);
* if (newValue != null)
* map.put(key, newValue);
* }
* }</pre>
*
* 默认实现对此方法的同步或原子性属性不提供任何保证。任何提供原子性保证的实现都必须覆盖此方法并记录其并发属性。特别是,所有subinterface java.util.concurrent.ConcurrentMap的实现都必须记录函数仅在值不存在时才以原子方式应用一次的情况。
*
* @param key 要与指定值关联的键
* @param mappingFunction 计算一个值的函数
* @return 与指定键关联的当前(现有或计算得到的)值,如果计算得到的值为null,则为null
* @throws NullPointerException 如果指定键为null并且此映射不支持null键,或者mappingFunction为null
* @throws UnsupportedOperationException 如果此映射不支持put操作
* (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
* @throws ClassCastException 如果指定键或值的类阻止将其存储在此映射中
* (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
* @since 1.8
*/
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;
}
/**
* 如果指定键的值存在且非null,则尝试使用给定键和其当前映射值计算新的映射。
*
* <p>如果函数返回null,则删除该映射。如果函数本身抛出(未检查的)异常,则重新抛出异常,并且当前映射保持不变。
*
* @implSpec
* 默认实现对于此map相当于以下步骤,然后返回当前值或null(如果不存在):
*
* <pre> {@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);
* }
* }</pre>
*
* 默认实现对此方法的同步或原子性属性不提供任何保证。任何提供原子性保证的实现都必须覆盖此方法并记录其并发属性。特别是,所有subinterface java.util.concurrent.ConcurrentMap的实现都必须记录函数仅在值不存在时才以原子方式应用一次的情况。
*
* @param key 与指定值关联的键
* @param remappingFunction 计算一个值的函数
* @return 与指定键关联的新值,如果没有则为null
* @throws NullPointerException 如果指定键为null,并且此映射不支持null键,或者remappingFunction为null
* @throws UnsupportedOperationException 如果此映射不支持put操作
* (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
* @throws ClassCastException 如果指定键或值的类阻止将其存储在此映射中
* (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
* @since 1.8
*/
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;
}
}
/**
* 尝试计算指定键及其当前映射值的映射(如果没有当前映射,则为null)。例如,要创建或附加一个{@code String msg}到一个值映射:
*
* <pre> {@code
* map.compute(key, (k, v) -> (v == null) ? msg : v.concat(msg))}</pre>
* (使用方法{@link #merge merge()}通常更简单。)
*
* <p>如果函数返回null,则删除该映射(或保持缺失状态,如果最初不存在)。如果函数本身抛出(未检查的)异常,则重新抛出异常,并且当前映射保持不变。
*
* @implSpec
* 默认实现对于此map相当于以下步骤,然后返回当前值或null(如果不存在):
*
* <pre> {@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;
* }
* }</pre>
*
* 默认实现对此方法的同步或原子性属性不提供任何保证。任何提供原子性保证的实现都必须覆盖此方法并记录其并发属性。特别是,所有subinterface java.util.concurrent.ConcurrentMap的实现都必须记录函数仅在值不存在时才以原子方式应用一次的情况。
*
* @param key 与指定值关联的键
* @param remappingFunction 计算一个值的函数
* @return 与指定键关联的新值,如果没有则为null
* @throws NullPointerException 如果指定键为null,并且此映射不支持null键,或者remappingFunction为null
* @throws UnsupportedOperationException 如果此映射不支持put操作
* (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
* @throws ClassCastException 如果指定键或值的类阻止将其存储在此映射中
* (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
* @since 1.8
*/
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) {
// 删除映射
if (oldValue != null || containsKey(key)) {
// 有要删除的内容
remove(key);
return null;
} else {
// 无事可做。保持现状。
return null;
}
} else {
// 添加或替换旧映射
put(key, newValue);
return newValue;
}
}
/**
* 如果指定键尚未与值关联或与null关联,则将其与给定非null值关联。否则,用给定重新映射函数的结果替换关联值,如果结果为null,则删除。当需要组合多个键值映射时,此方法可能很有用。例如,要创建或附加一个{@code String msg}到一个值映射:
*
* <pre> {@code
* map.merge(key, msg, String::concat)
* }</pre>
*
* <p>如果函数返回null,则删除该映射。如果函数本身抛出(未检查的)异常,则重新抛出异常,并且当前映射保持不变。
*
* @implSpec
* 默认实现对于此map相当于以下步骤,然后返回当前值或null(如果不存在):
*
* <pre> {@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);
* }</pre>
*
* 默认实现对此方法的同步或原子性属性不提供任何保证。任何提供原子性保证的实现都必须覆盖此方法并记录其并发属性。特别是,所有subinterface java.util.concurrent.ConcurrentMap的实现都必须记录函数仅在值不存在时才以原子方式应用一次的情况。
*
* @param key 要与生成的值关联的键
* @param value 与键关联的非空值
* @param remappingFunction 重新映射值的函数
* @return 与指定键关联的新值,如果没有关联值,则为null
* @throws UnsupportedOperationException 如果此映射不支持put操作
* (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
* @throws ClassCastException 如果指定键或值的类阻止将其存储在此映射中
* (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
* @throws NullPointerException 如果指定键为null,并且此映射不支持null键,或者value或remappingFunction为null
* @since 1.8
*/
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;
}