Map容器家族(Map接口源码详解)
一、在Map集合体系中的位置及概述
Map接口式是Map集合体系的顶层接口,它定义和规范了该集合体系的大体规范,所有子类该实现的方法。该体系是以键值对(key-value)对位元素进行存储。
描述:
(1)映射(map)是一个存储键、键值对的对象,给定一个键,可以查询得到它的值,键和值都可以是对象。
(2)键必须是唯一的,值可以重复(Map接口映射唯一的键到值)
(3)有些映射可以接收null键和null值,而有的不行
(4)下面的接口支持映射:
Map接口 映射唯一关键字给值
Map.Entry接口 描述映射中的元素(关键字/值对),这是Map的一个内部类
SortedMap接口 扩展Map以便关键字按升序保持
(5)键(Key)是以后用于检索值的对象,给定一个键和一个值,可以存储这个值到一个Map对象中,以后可以
使用对应的键来检索它
二、约束子类必须实现的方法
1.查询操作(Query Operations)
/**
* 功能:查询元素个数
*/
int size();
/**
* 功能:是否为空
*/
boolean isEmpty();
/**
* 功能:判断键是否存在
*/
boolean containsKey(Object key);
/**
* 功能:判断值是否存在
*/
boolean containsValue(Object value);
/**
* 功能:根据键获取值
*/
V get(Object key);
2.修改操作(Modification Operations)
/**
* 功能:向map集合中添加键值对,如果键原来没有返回null,如果有返回被替换的值
*/
V put(K key, V value);
/**
* 功能:根据key移除键值对,并返回值
*/
V remove(Object key);
3.批量操作(Bulk Operations)
/**
* 功能:批量添加map集合
*/
void putAll(Map<? extends K, ? extends V> m);
/**
* 功能:清空map集合中的所有键值对
*/
void clear();
4.视图操作(Views)
/**
* 功能:返回map集合中所有键的Set集合
*/
Set<K> keySet();
/**
* 功能:返回map集合中所有值的Collection集合
*/
Collection<V> values();
/**
* 功能:返回map集合中所有键值对的Entry表示形式的Set集合
*/
Set<Map.Entry<K, V>> entrySet();
5.键值对视图接口(interface Entry<K,V>),该接口的方法如下:
该接口的作用是保存一个Key和Value键值对,和约束了一些操作键值的方法。
/**
* 功能:获取键
*/
K getKey();
/**
* 功能:获取值
*/
V getValue();
/**
* 功能:设置键的值
*/
V setValue(V value);
/**
* 功能:比较是否相等
*/
boolean equals(Object o);
/**
* 功能:返回hash码
*/
int hashCode();
JDK1.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());
}
6.比较和哈希(Comparison and hashing)
/**
* 功能:比较是否相等
*/
boolean equals(Object o);
/**
* 功能:返回哈希值
*/
int hashCode();
7.JDK1.8新添加的default方法(Defaultable methods)
default关键字是jdk1.8新添加的,它打破了接口中的方法不能有方法体的标准。
(1)获取方法 getOrDefault
/**
* 功能:返回指定键映射到的值,如果此映射不包含键的映射,则返回指定的默认值
*/
default V getOrDefault(Object key, V defaultValue) {
V v;
return (((v = get(key)) != null) || containsKey(key))
? v
: defaultValue;
}
(2)遍历方法 forEcah
/**
* 功能:对每个键值对执行相同的操作,该操作使用lambda表达式定义
* 注:BiConsumer函数式接口为两个输入
*/
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);
}
}
使用方法演示
Map<Integer, Integer> map = new HashMap<>();
map.put(1, 1);
map.put(2, 2);
map.put(3, 3);
map.forEach((x1, x2) -> System.out.println(x1 + "=" + x2));
(3)对所有值进行操作 raplaceAll
/**
* 功能:根据指定的操作,对所有键的值进行相同的操作
* 注:BiFunction函数值接口为两个输入,一个输出
*/
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);
}
}
}
使用方法演示
Map<Integer, Integer> map = new HashMap<>();
map.put(1, 1);
map.put(2, 2);
map.put(3, 3);
// x1和x2分别为键和值的变量,x1+x2为对所有值的操作
map.replaceAll((x1, x2) -> x1 + x2);
map.forEach((x1, x2) -> System.out.println(x1 + "=" + x2));
(4)添加元素 putIfAbsent
/**
* 功能:如果map中没有指定的键,则put进该键值对
*/
default V putIfAbsent(K key, V value) {
V v = get(key);
if (v == null) {
v = put(key, value);
}
return v;
}
(5)删除功能 remove
/**
* 功能:移除指定的键值对
*/
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;
}
(6)替换功能
/**
* 功能:替换
*/
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;
}
三、总结
Map接口是对键值对形式存储的数据结构方法的约束,子类方法需要实现其规定的抽象方法。在JDK1.8之后又添加了一些default声明的方法去扩充它本身的方法,新添加的方法中有很大一部分是关于1.8后新引入的函数式接口的方法,我个人认使用Lambda表达式这在很大程度上简化了编程代码的代码量。