一、概述
在Map中存储的每一个键值对都是以一个Map.Entry<K,V>的实现对象存储的,Map.Entry是一个接口,不能实例化,所以Map的不同实现类,存储键值对的方式也会有所不同,只要这个键值对的类实现了Map.Entry接口即可。
在HashMap中键值对的存储方式有两种:
其中一种是我们比较熟知的链表存储结构,也就是以HashMap.Node<K,V>类的对象方式存储的, Node类是HashMap的一个静态内部类,实现了 Map.Entry<K,V>接口。在调用put方法创建一个新的键值对时,会调用newNode方法来创建Node对象。
二、方法解析
// 该方法很简单,只是调用了Node类的构造函数
Node<K,V> newNode(int hash, K key, V value, Node<K,V> next) {
return new Node<>(hash, key, value, next);
}
我们接下来看下Node类的代码
/**
* 该类只实现了 Map.Entry 接口,
* 所以该类只需要实现getKey、getValue、setValue三个方法即可
* 除此之外以什么样的方式来组织数据,就和接口无关了
*/
static class Node<K,V> implements Map.Entry<K,V> {
final int hash; // hash值,不可变
final K key; // 键,不可变
V value; // 值
Node<K,V> next; // 下一个节点
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
// 实现接口定义的方法,且该方法不可被重写
public final K getKey() { return key; }
// 实现接口定义的方法,且该方法不可被重写
public final V getValue() { return value; }
public final String toString() { return key + "=" + value; }
// 重写父类Object的hashCode方法,且该方法不可被自己的子类再重写
// 返回:key的hashCode值和value的hashCode值进行异或运算结果
public final int hashCode() {
return Objects.hashCode(key) ^ Objects.hashCode(value);
}
// 实现接口定义的方法,且该方法不可被重写
// 设值,返回旧值
public final V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}
/*
* 重写父类Object的equals方法,且该方法不可被自己的子类再重写
* 判断相等的依据是,只要是Map.Entry的一个实例,并且键键、值值都相等就返回True
*/
public final boolean equals(Object o) {
if (o == this)
return true;
if (o instanceof Map.Entry) {
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
if (Objects.equals(key, e.getKey()) &&
Objects.equals(value, e.getValue()))
return true;
}
return false;
}
}
三、小贴士
在上一个小节中存在着Objects.equals、Objects.hashCode这种静态方法,其内部实现都特别简单。
以下是截取Objects类中这两个方法的实现。
public static int hashCode(Object o) {
return o != null ? o.hashCode() : 0;
}
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}
最主要的帮助用户节省了空值判断和指针相同判断。