/**
*基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。
*(除了非同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同。)
*此类不保证映射的顺序,特别是它不保证该顺序恒久不变。
*此实现假定哈希函数将元素适当地分布在各桶之间,可为基本操作(get 和 put)提供稳定的性能。
*迭代 collection 视图所需的时间与 HashMap 实例的“容量”(桶的数量)及其大小(键-值映射关系数)成比例。
*所以,如果迭代性能很重要,则不要将初始容量设置得太高(或将加载因子设置得太低)。
*HashMap 的实例有两个参数影响其性能:初始容量 和加载因子。
*容量 是哈希表中桶的数量,初始容量只是哈希表在创建时的容量。
*加载因子 是哈希表在其容量自动增加之前可以达到多满的一种尺度。
*当哈希表中的条目数超出了加载因子与当前容量的乘积时,则要对该哈希表进行 rehash 操作(即重建内部数据结构),
*从而哈希表将具有大约两倍的桶数。
*通常,默认加载因子 (.75) 在时间和空间成本上寻求一种折衷。
*加载因子过高虽然减少了空间开销,但同时也增加了查询成本(在大多数 HashMap 类的操作中,包括 get 和 put 操作,都反映了这一点)。
*在设置初始容量时应该考虑到映射中所需的条目数及其加载因子,以便最大限度地减少 rehash 操作次数。
*如果初始容量大于最大条目数除以加载因子,则不会发生 rehash 操作。
*如果很多映射关系要存储在 HashMap 实例中,则相对于按需执行自动的 rehash 操作以增大表的容量来说,
*使用足够大的初始容量创建它将使得映射关系能更有效地存储。
*注意,此实现不是同步的。如果多个线程同时访问一个哈希映射,
*而其中至少一个线程从结构上修改了该映射,则它必须 保持外部同步。
*(结构上的修改是指添加或删除一个或多个映射关系的任何操作;仅改变与实例已经包含的键关联的值不是结构上的修改。)
*这一般通过对自然封装该映射的对象进行同步操作来完成。如果不存在这样的对象,
*则应该使用 Collections.synchronizedMap 方法来“包装”该映射。最好在创建时完成这一操作,
*以防止对映射进行意外的非同步访问,如下所示:
* Map m = Collections.synchronizedMap(new HashMap(...));
*由所有此类的“collection 视图方法”所返回的迭代器都是快速失败 的:
*在迭代器创建之后,如果从结构上对映射进行修改,除非通过迭代器本身的 remove 方法,
*其他任何时间任何方式的修改,迭代器都将抛出 ConcurrentModificationException。
*因此,面对并发的修改,迭代器很快就会完全失败,而不冒在将来不确定的时间发生任意不确定行为的风险。
*注意,迭代器的快速失败行为不能得到保证,一般来说,存在非同步的并发修改时,不可能作出任何坚决的保证。
*快速失败迭代器尽最大努力抛出 ConcurrentModificationException。
*因此,编写依赖于此异常的程序的做法是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测程序错误。
* @param <K> 此映射所维护的键的类型
* @param <V> 所映射值的类型
*/
public class HashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable
{
// 默认初始容量-必须是2的幂。
static final int DEFAULT_INITIAL_CAPACITY = 16;
/*
* 最大容量,如果任何一个构造函数用参数隐式指定了更高的值,则使用。
* 必须是2的幂<= 2的30次方。
*/
static final int MAXIMUM_CAPACITY = 1 << 30;
/*
* 在构造函数中没有指定时使用的加载因子。
* 即:默认的加载因子
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;
// 根据需要调整表的大小。长度必须总是2的幂。
transient Entry<K,V>[] table;
// 此映射中包含的键值映射的数量。
transient int size;
// 要调整大小的下一个大小值(容量(capacity )*负载系数(loadFactor))。
int threshold;
// 哈希表的负载因子。
final float loadFactor;
/*
* 这个HashMap在结构上被修改的次数。
* 结构修改是指改变HashMap中映射的次数,或者以其他方式修改其内部结构(例如,rehash)。
* 此字段用于使HashMap的集合视图(Collection-views)上的迭代器快速失败(fail-fast)。
*/
transient int modCount;
/*
* 映射容量的默认阈值,超过此阈值,字符串键将使用替代散列(alternative hashing)。
* 替代哈希(alternative hashing)减少了由于字符串键的弱哈希码计算而导致的冲突发生率。
*/
static final int ALTERNATIVE_HASHING_THRESHOLD_DEFAULT = Integer.MAX_VALUE;
/**
* 在虚拟机被启动之前,它的值是不能被初始化的。
*/
private static class Holder {
// Unsafe 结构
/**
* Unsafe 工具
*/
static final sun.misc.Unsafe UNSAFE;
/**
* 我们必须在readObject()方法中设置“final”hashSeed字段的偏移量。
*/
static final long HASHSEED_OFFSET;
/**
* 表容量大于此容量时,可切换为使用替代哈希(alternative hashing)。
*/
static final int ALTERNATIVE_HASHING_THRESHOLD;
static {// 启用特权,执行指定的 GetPropertyAction。
String altThreshold = java.security.AccessController.doPrivileged(
new sun.security.action.GetPropertyAction(
"jdk.map.althashing.threshold"));
int threshold;
try {
threshold = (null != altThreshold)
? Integer.parseInt(altThreshold)
: ALTERNATIVE_HASHING_THRESHOLD_DEFAULT;
// 如果是-1,禁用替代哈希(alternative hashing)
if (threshold == -1) {
threshold = Integer.MAX_VALUE;
}
if (threshold < 0) { // 值必须是正整数
throw new IllegalArgumentException("value must be positive integer.");
}
} catch(IllegalArgumentException failed) {
throw new Error("Illegal value for 'jdk.map.althashing.threshold'", failed);
}
ALTERNATIVE_HASHING_THRESHOLD = threshold;
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
HASHSEED_OFFSET = UNSAFE.objectFieldOffset(
HashMap.class.getDeclaredField("hashSeed"));
} catch (NoSuchFieldException | SecurityException e) {
throw new Error("Failed to record hashSeed offset", e);
}
}
}
// 如果true,则执行字符串键的替代散列( alternative hashing),以减少由于弱散列代码计算而导致的冲突。
transient boolean useAltHashing;
// 与此实例关联的随机值,应用于键的哈希码以使哈希冲突更难找到。
transient final int hashSeed = sun.misc.Hashing.randomHashSeed(this);
/**
* 构造一个带指定初始容量和加载因子的空 HashMap。
* @param initialCapacity 初始容量
* @param loadFactor 加载因子
*/
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0) // 初始容量为负
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)// 初始容量大于最大容量
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))// 加载因子为非正
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
// 求 2的幂 >= 初始容量(initialCapacity)
int capacity = 1;
while (capacity < initialCapacity)
capacity <<= 1;// capacity左移1位,相当于 capacity = capacity << 1
this.loadFactor = loadFactor;
// 获取capacity * loadFactor 和MAXIMUM_CAPACITY + 1 中的较小者
threshold = (int)Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
table = new Entry[capacity];
useAltHashing = sun.misc.VM.isBooted() &&
(capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
init();// 用于子类初始化钩子
}
/**
* 构造一个带指定初始容量和默认加载因子 (0.75) 的空 HashMap。
* @param initialCapacity 初始容量
*/
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
/**
* 构造一个具有默认初始容量 (16) 和默认加载因子 (0.75) 的空 HashMap。
*/
public HashMap() {
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
}
/**
* 构造一个映射关系与指定 Map 相同的新 HashMap。
* 所创建的 HashMap 具有默认加载因子 (0.75) 和足以容纳指定 Map 中映射关系的初始容量。
* @param m 映射,其映射关系将存放在此映射中
*/
public HashMap(Map<? extends K, ? extends V> m) {
this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
putAllForCreate(m);// 遍历m,把m的[key,value]加入到当前对象关联的table中
}
// 内部工具
/**
* 子类的初始化钩子。在HashMap初始化之后但在插入任何条目(entries)之前,
* 在所有构造函数和伪构造函数(pseudo-constructors)(clone、readObject)中调用此方法。
* (如果没有这种方法,readObject将需要显式的子类信息。)
*/
void init() {
}
/**
* 检索对象哈希码,并对结果哈希应用一个补充哈希函数,这可以防止哈希函数质量差。
* 这一点非常关键,因为HashMap使用长度为2的幂的哈希表,
* 否则的话,对于在低位没有差别的哈希代码,哈希表会遇到冲突。
* 注意:空键总是映射到哈希0,因此索引为0。
* @param k
* @return
*/
final int hash(Object k) {
int h = 0;
if (useAltHashing) {// true,执行字符串键的替代散列( alternative hashing),以减少由于弱散列代码计算而导致的冲突。
if (k instanceof String) {
return sun.misc.Hashing.stringHash32((String) k);
}
h = hashSeed;
}
h ^= k.hashCode(); // 相当于h=h^k.hashCode(),按二进制位进行“异或”运算
// 这个函数确保在每个bit位置上只有常数倍差异的哈希码具有有限的冲突数(在默认负载系数下大约为8)。
h ^= (h >>> 20) ^ (h >>> 12);// 无符号右移20位^无符号右移12位
return h ^ (h >>> 7) ^ (h >>> 4);// 无符号右移7位^无符号右移4位
}
/**
* 返回哈希码h的索引。
* 即:table[]中的索引位置。
* @param h 哈希值
* @param length 长度
* @return
*/
static int indexFor(int h, int length) {
return h & (length-1);// 二进制位进行“与”运算
}
/**
* 返回此映射中的键-值映射关系数
* 覆盖:类 AbstractMap<K,V> 中的 size
* @return 此映射中的键-值映射关系数
*/
public int size() {
return size;
}
/**
* 如果此映射不包含键-值映射关系,则返回 true。
* 覆盖:类 AbstractMap<K,V> 中的 isEmpty
* @return 如果此映射不包含键-值映射关系,则返回 true
*/
public boolean isEmpty() {
return size == 0;
}
/**
* 返回指定键所映射的值;如果对于该键来说,此映射不包含任何映射关系,则返回 null。
* 更确切地讲,如果此映射包含一个满足 (key==null ? k==null : key.equals(k)) 的从 k 键到 v 值的映射关系,
* 则此方法返回 v;否则返回 null。(最多只能有一个这样的映射关系。)
* 返回 null 值并不一定 表明该映射不包含该键的映射关系;也可能该映射将该键显示地映射为 null。
* 可使用 containsKey 操作来区分这两种情况。
* 覆盖:类 AbstractMap<K,V> 中的 get
* @param key 要返回其关联值的键
* @return 指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null
*/
public V get(Object key) {
if (key == null)
return getForNullKey();// 获取键为null的值。
Entry<K,V> entry = getEntry(key);// 返回与HashMap中key关联的项。
return null == entry ? null : entry.getValue();
}
/**
* 获取键为null的值。
* 卸载版本(Offloaded version)的get()来查找空键。空键映射到索引0。
* 为了在两个最常用的操作(get和put)中提高性能,这个空的情况被拆分为不同的方法,
* 但是与其他操作中的条件语句结合在一起。
* @return
*/
private V getForNullKey() {
for (Entry<K,V> e = table[0]; e != null; e = e.next) {
if (e.key == null)
return e.value;
}
return null;
}
/**
* 如果此映射包含对于指定键的映射关系,则返回 true。
* 覆盖:类 AbstractMap<K,V> 中的 containsKey
* @param key 要测试其是否在此映射中存在的键
* @return 如果此映射包含对于指定键的映射关系,则返回 true。
*/
public boolean containsKey(Object key) {
return getEntry(key) != null;
}
/**
* 返回与HashMap中指定键关联的项。如果HashMap不包含键的映射,则返回null。
* @param key 要返回其关联值的键
* @return
*/
final Entry<K,V> getEntry(Object key) {
int hash = (key == null) ? 0 : hash(key);
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))// key值和hash一致
return e;
}
return null;
}
/**
* 在此映射中关联指定值与指定键。如果该映射以前包含了一个该键的映射关系,则旧值被替换。
* 覆盖:类 AbstractMap<K,V> 中的 put
* @param key 指定值将要关联的键
* @param value 指定键将要关联的值
* @return 与 key 关联的旧值;如果 key 没有任何映射关系,则返回 null。(返回 null 还可能表示该映射之前将 null 与 key 关联。)
*/
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key);
int i = indexFor(hash, table.length);// 返回哈希码hash的索引。
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
// 将具有指定键(key)、值(value)和散列代码(hash)的新条目添加到指定的桶(bucket)(数组位置i)中。
addEntry(hash, key, value, i);
return null;
}
/**
* 键为null的存值。
* 卸载版本(Offloaded version )的为空键的put()方法
* @param value 要存的值
* @return 返回原来键为null的值
*/
private V putForNullKey(V value) {
for (Entry<K,V> e = table[0]; e != null; e = e.next) {
if (e.key == null) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);// 方法为空,类似于钩子,需要覆写
return oldValue;
}
}
modCount++;
// 将具有指定键(null)、值(value)和散列代码(0)的新项(entry)添加到指定的桶(bucket)(数组索引位置0)中。
addEntry(0, null, value, 0);
return null;
}
/**
* 此方法由构造函数和伪构造函数(clone, readObject)代替put。
* 它不调整表的大小,不检查comodification,等等。它调用createEntry而不是addEntry。
* @param key
* @param value
*/
private void putForCreate(K key, V value) {
int hash = null == key ? 0 : hash(key);
int i = indexFor(hash, table.length);// 返回哈希码hash的索引。
/*
* 查找键的现有条目。克隆或反序列化永远不会发生这种情况。
* 如果输入映射是顺序不一致的排序映射,则其仅在构造上发生。
*/
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) {// key和hash值一样
e.value = value;
return;
}
}
// 将具有指定键(key)、值(value)和散列代码(hash)的新条目添加到table中指定的位置(i)中。
createEntry(hash, key, value, i);
}
/**
* 遍历m,把m的[key,value]加入到当前对象关联的table中
* @param m 映射,其映射关系将存放在此映射中
*/
private void putAllForCreate(Map<? extends K, ? extends V> m) {
for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
putForCreate(e.getKey(), e.getValue());
}
/**
* 将此映射的内容重新散列到容量更大的新数组中。当映射中的键数达到阈值时,将自动调用此方法。
* @param newCapacity 新容量
*/
void resize(int newCapacity) {
Entry[] oldTable = table;
int oldCapacity = oldTable.length;
if (oldCapacity == MAXIMUM_CAPACITY) {// 旧容量等于最大容量
threshold = Integer.MAX_VALUE;
return;
}
Entry[] newTable = new Entry[newCapacity];
boolean oldAltHashing = useAltHashing;
// useAltHashing = useAltHashing | (sun.misc.VM.isBooted() && (newCapacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD))
useAltHashing |= sun.misc.VM.isBooted() &&
(newCapacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
boolean rehash = oldAltHashing ^ useAltHashing;
transfer(newTable, rehash);// 将所有条目从当前表传输到newTable。
table = newTable;
// 获取newCapacity * loadFactor 和MAXIMUM_CAPACITY + 1 中的较小者
threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
}
/**
* 将所有条目从当前表传输到newTable。
* @param newTable 与当前对象相关联的新table[]
* @param rehash 是否计算条目(entry)的哈希值
*/
void transfer(Entry[] newTable, boolean rehash) {
int newCapacity = newTable.length;
for (Entry<K,V> e : table) {// 循环添加
while(null != e) {
Entry<K,V> next = e.next;
if (rehash) {// 是否计算条目(entry)的哈希值
e.hash = null == e.key ? 0 : hash(e.key);
}
int i = indexFor(e.hash, newCapacity);// 返回哈希码e.hash的索引。
e.next = newTable[i];
newTable[i] = e;
e = next;
}
}
}
/**
* 将指定映射的所有映射关系复制到此映射中,这些映射关系将替换此映射目前针对指定映射中所有键的所有映射关系。
* 覆盖:类 AbstractMap<K,V> 中的 putAll
* @param m 要在此映射中存储的映射关系
*/
public void putAll(Map<? extends K, ? extends V> m) {
int numKeysToBeAdded = m.size();// 获取此映射中的键值映射的数量。
if (numKeysToBeAdded == 0)
return;
/*
* 如果要添加的映射数量大于或等于阈值(threshold),则扩展映射。这是保守的;
* 最明显的条件是(m.size() + size) >= threshold,但是如果要添加的键与该映射中已经存在的键重叠,
* 则该条件可能导致具有两倍适当容量(capacity)的映射。
* 通过使用保守的计算方法,我们让自己最多再调整一次大小。
*/
if (numKeysToBeAdded > threshold) {// 添加的映射数量大于阈值
int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1);// 添加的映射的容量
if (targetCapacity > MAXIMUM_CAPACITY)// 添加的映射容量大于最大容量
targetCapacity = MAXIMUM_CAPACITY;
int newCapacity = table.length;// 求 2的幂 >= 添加的映射容量
while (newCapacity < targetCapacity)
newCapacity <<= 1;// newCapacity左移1位,相当于 newCapacity = newCapacity << 1
if (newCapacity > table.length)
resize(newCapacity);// 将此映射的内容重新散列到容量更大(newCapacity)的新数组中。
}
// 循环,存放[key,value]
for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
put(e.getKey(), e.getValue());
}
/**
* 从此映射中移除指定键的映射关系(如果存在)。
* 覆盖:类 AbstractMap<K,V> 中的 remove
* @param key 其映射关系要从映射中移除的键
* @return V 与 key 关联的旧值;如果 key 没有任何映射关系,则返回 null。(返回 null 还可能表示该映射之前将 null 与 key 关联。)
*/
public V remove(Object key) {
Entry<K,V> e = removeEntryForKey(key);// 删除并返回与HashMap中key关联的条目。
return (e == null ? null : e.value);
}
/**
* 删除并返回与HashMap中指定键关联的条目。如果HashMap不包含此键的映射,则返回null。
* @param key 其映射关系要从映射中移除的键
* @return
*/
final Entry<K,V> removeEntryForKey(Object key) {
int hash = (key == null) ? 0 : hash(key);
int i = indexFor(hash, table.length);// 返回哈希码hash的索引。
Entry<K,V> prev = table[i];
Entry<K,V> e = prev;
while (e != null) {// 获取元素非空
Entry<K,V> next = e.next;// 下一个条目
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) {// hash和key值一致,改变元素数、在结构上被修改的次数、重新在位置赋值
modCount++;
size--;
if (prev == e)
table[i] = next;
else
prev.next = next;
e.recordRemoval(this);// 方法为空,类似于钩子,需要覆写
return e;
}
prev = e;
e = next;
}
return e;
}
/**
* 移除EntrySet的特殊版本,使用Map.Entry.equals()方法进行匹配。
* @param o
* @return
*/
final Entry<K,V> removeMapping(Object o) {
if (!(o instanceof Map.Entry))
return null;
Map.Entry<K,V> entry = (Map.Entry<K,V>) o;
Object key = entry.getKey();
int hash = (key == null) ? 0 : hash(key);
int i = indexFor(hash, table.length);// 返回哈希码hash的索引。
Entry<K,V> prev = table[i];
Entry<K,V> e = prev;
while (e != null) {// 获取元素非空
Entry<K,V> next = e.next;
if (e.hash == hash && e.equals(entry)) {// hash、条目一致,改变元素数、在结构上被修改的次数、重新在位置赋值
modCount++;
size--;
if (prev == e)
table[i] = next;
else
prev.next = next;
e.recordRemoval(this);// 方法为空,类似于钩子,需要覆写
return e;
}
prev = e;
e = next;
}
return e;
}
/**
* 从此映射中移除所有映射关系。此调用返回后,映射将为空。
* 覆盖:类 AbstractMap<K,V> 中的 clear
*/
public void clear() {
modCount++;
Entry[] tab = table;
for (int i = 0; i < tab.length; i++)
tab[i] = null;
size = 0;
}
/**
* 如果此映射将一个或多个键映射到指定值,则返回 true。
* 覆盖:类 AbstractMap<K,V> 中的 containsValue
* @param value 要测试其是否在此映射中存在的值
* @return 如果此映射将一个或多个键映射到指定值,则返回 true
*/
public boolean containsValue(Object value) {
if (value == null)//值为空处理
return containsNullValue();
Entry[] tab = table;
for (int i = 0; i < tab.length ; i++)
for (Entry e = tab[i] ; e != null ; e = e.next)
if (value.equals(e.value))
return true;
return false;
}
/**
* 包含空参数的containsValue的特殊情况代码
* @return
*/
private boolean containsNullValue() {
Entry[] tab = table;
for (int i = 0; i < tab.length ; i++)
for (Entry e = tab[i] ; e != null ; e = e.next)
if (e.value == null)
return true;
return false;
}
/**
* 返回此 HashMap 实例的浅拷贝:并不复制键和值本身。
* 覆盖:类 AbstractMap<K,V> 中的 clone
* @return 此映射的浅表副本
*/
public Object clone() {
HashMap<K,V> result = null;
try {
result = (HashMap<K,V>)super.clone();
} catch (CloneNotSupportedException e) {
// assert false;
}
result.table = new Entry[table.length];
result.entrySet = null;
result.modCount = 0;
result.size = 0;
result.init();// 子类的初始化钩子。
result.putAllForCreate(this); // 遍历this,把this的[key,value]加入到result关联的table中
return result;
}
/**
* 映射项(键-值对)。
* Map.entrySet 方法返回映射的 collection 视图,其中的元素属于此类。
* 获得映射项引用的唯一 方法是通过此 collection 视图的迭代器来实现。
* 这些 Map.Entry 对象仅 在迭代期间有效;
* 更确切地讲,如果在迭代器返回项之后修改了底层映射,则某些映射项的行为是不确定的,除了通过 setValue 在映射项上执行操作之外。
* @param <K> 此映射所维护的键的类型
* @param <V> 所映射值的类型
*/
static class Entry<K,V> implements Map.Entry<K,V> {
final K key;// 键
V value;// 值
Entry<K,V> next;// 下一个条目
int hash;// 哈希值
/**
* 创建新条目。
* @param h 哈希值
* @param k 键
* @param v 值
* @param n 下一个条目
*/
Entry(int h, K k, V v, Entry<K,V> n) {
value = v;
next = n;
key = k;
hash = h;
}
/**
* 返回与此项对应的键。
* @return 与此项对应的键
*/
public final K getKey() {
return key;
}
/**
* 返回与此项对应的值。如果已经从底层映射中移除了映射关系(通过迭代器的 remove 操作),则此调用的结果是不确定的。
* @return 与此项对应的值
*/
public final V getValue() {
return value;
}
/**
* 用指定的值替换与此项对应的值(可选操作)。(写入该映射。)
* 如果已经从映射中移除了映射关系(通过迭代器的 remove 操作),则此调用的行为是不确定的。
* @param newValue 要存储在此项中的新值
* @return 与此项对应的旧值
*/
public final V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}
/**
* 比较指定对象与此项的相等性。如果给定对象也是一个映射项,并且两个项表示相同的映射关系,则返回 true。
* 覆盖:类 Object 中的 equals
* @param o 要与此映射项进行相等性比较的对象
* @return 如果指定的对象等于此映射项,则返回 true
*/
public final boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry e = (Map.Entry)o;
Object k1 = getKey();
Object k2 = e.getKey();
if (k1 == k2 || (k1 != null && k1.equals(k2))) {
Object v1 = getValue();
Object v2 = e.getValue();
if (v1 == v2 || (v1 != null && v1.equals(v2)))
return true;
}
return false;
}
/**
* 返回此映射项的哈希码值。
* 覆盖:类 Object 中的 hashCode
* @return 映射项的哈希码值
*/
public final int hashCode() {
return (key==null ? 0 : key.hashCode()) ^
(value==null ? 0 : value.hashCode());
}
public final String toString() {
return getKey() + "=" + getValue();
}
/**
* 每当条目中的值被HashMap中键k的put(k,v)调用覆盖时,就会调用此方法。
*/
void recordAccess(HashMap<K,V> m) {
}
/**
* 每当从表中删除条目时,都会调用此方法。
*/
void recordRemoval(HashMap<K,V> m) {
}
}
/**
* 将具有指定键、值和散列代码的新条目添加到指定的桶(bucket)中。此方法负责在适当的情况下调整表的大小。
* @param hash 根据键计算的哈希值
* @param key 指定值将要关联的键
* @param value 指定键将要关联的值
* @param bucketIndex 在table[]数组上的索引
*/
void addEntry(int hash, K key, V value, int bucketIndex) {
if ((size >= threshold) && (null != table[bucketIndex])) {
resize(2 * table.length);// 将此映射的内容重新散列到容量更大(2 * table.length)的新数组中。
hash = (null != key) ? hash(key) : 0;// 计算hash值
bucketIndex = indexFor(hash, table.length);// 获取哈希码hash的索引。
}
// 将具有指定键(key)、值(value)和散列代码(hash)的新条目添加到table中指定的位置(bucketIndex)中。
createEntry(hash, key, value, bucketIndex);
}
/**
* 将具有指定键、值和散列代码的新条目添加到table中指定的位置中。
* 注意,添加时不调整表的大小,不检查comodification等。
* @param hash 根据键计算的哈希值
* @param key 指定值将要关联的键
* @param value 指定键将要关联的值
* @param bucketIndex 在table[]数组上的索引
*/
void createEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V> e = table[bucketIndex];
table[bucketIndex] = new Entry<>(hash, key, value, e);
size++;
}
private abstract class HashIterator<E> implements Iterator<E> {
Entry<K,V> next; // 返回下一条目
int expectedModCount; // 对于快速失败
int index; // 当前的位置
Entry<K,V> current; // 当前的条目
HashIterator() {
expectedModCount = modCount;
if (size > 0) { // 先进先出
Entry[] t = table;
while (index < t.length && (next = t[index++]) == null)
;
}
}
/**
* 如果仍有元素可以迭代,则返回 true
* @return 如果迭代器具有多个元素,则返回 true
*/
public final boolean hasNext() {
return next != null;
}
/**
* 返回下一个条目
* @return
*/
final Entry<K,V> nextEntry() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
Entry<K,V> e = next;
if (e == null)
throw new NoSuchElementException();
if ((next = e.next) == null) {
Entry[] t = table;
while (index < t.length && (next = t[index++]) == null)
;
}
current = e;
return e;
}
/**
* 从迭代器指向的 collection 中移除迭代器返回的最后一个元素(可选操作)。
*/
public void remove() {
if (current == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
Object k = current.key;
current = null;
// 调用当前对象的方法,删除并返回与HashMap中指定键(k)关联的条目。
HashMap.this.removeEntryForKey(k);
expectedModCount = modCount;
}
}
/**
* 值迭代器
*/
private final class ValueIterator extends HashIterator<V> {
/**
* 返回迭代的下一个条目的元素值。
*/
public V next() {
return nextEntry().value;
}
}
/**
* 键迭代器
*/
private final class KeyIterator extends HashIterator<K> {
/**
* 返回迭代的下一个条目的键。
*/
public K next() {
return nextEntry().getKey();
}
}
/**
* 条目迭代器
*/
private final class EntryIterator extends HashIterator<Map.Entry<K,V>> {
/**
* 返回迭代的下一个条目
*/
public Map.Entry<K,V> next() {
return nextEntry();
}
}
// 子类重写这些来更改视图的iterator()方法的行为
Iterator<K> newKeyIterator() {
return new KeyIterator();
}
Iterator<V> newValueIterator() {
return new ValueIterator();
}
Iterator<Map.Entry<K,V>> newEntryIterator() {
return new EntryIterator();
}
// 视图
private transient Set<Map.Entry<K,V>> entrySet = null;
/**
* 返回此映射中所包含的键的 Set 视图。该 set 受映射的支持,所以对映射的更改将反映在该 set 中,反之亦然。
* 如果在对 set 进行迭代的同时修改了映射(通过迭代器自己的 remove 操作除外),则迭代结果是不确定的。
* 该 set 支持元素的移除,通过 Iterator.remove、Set.remove、removeAll、retainAll
* 和 clear 操作可从该映射中移除相应的映射关系。它不支持 add 或 addAll 操作。
* 覆盖:类 AbstractMap<K,V> 中的 keySet
* @return 此映射中包含的键的 set 视图
*/
public Set<K> keySet() {
Set<K> ks = keySet;
return (ks != null ? ks : (keySet = new KeySet()));
}
/**
* 此映射中所包含的键的 Set 视图。
*/
private final class KeySet extends AbstractSet<K> {
/**
* 键迭代器
* @return
*/
public Iterator<K> iterator() {
return newKeyIterator();
}
/**
* 返回此映射中包含的键值映射的数量。
* @return
*/
public int size() {
return size;
}
/**
* 如果此映射包含对于指定键的映射关系,则返回 true。
* @param o 要测试其是否在此映射中存在的键
* @return 如果此映射包含对于指定键的映射关系,则返回 true。
*/
public boolean contains(Object o) {
return containsKey(o);
}
/**
* 删除并返回与HashMap中指定键关联的条目。
* @param o 其映射关系要从映射中移除的键
* @return
*/
public boolean remove(Object o) {
return HashMap.this.removeEntryForKey(o) != null;
}
/**
* 从此映射中移除所有映射关系。
*/
public void clear() {
HashMap.this.clear();
}
}
/**
* 返回此映射所包含的值的 Collection 视图。
* 该 collection 受映射的支持,所以对映射的更改将反映在该 collection 中,反之亦然。
* 如果在对 collection 进行迭代的同时修改了映射(通过迭代器自己的 remove 操作除外),则迭代结果是不确定的。
* 该 collection 支持元素的移除,通过 Iterator.remove、Collection.remove、removeAll、
* retainAll 和 clear 操作可从该映射中移除相应的映射关系。它不支持 add 或 addAll 操作。
* 覆盖:类 AbstractMap<K,V> 中的 values
* @return 此映射中包含的值的 collection 视图
*/
public Collection<V> values() {
Collection<V> vs = values;
return (vs != null ? vs : (values = new Values()));
}
/**
* 此映射所包含的值的 Collection 视图
*/
private final class Values extends AbstractCollection<V> {
/**
* 值迭代器
*/
public Iterator<V> iterator() {
return newValueIterator();
}
/**
* 返回此映射中包含的键值映射的数量。
* @return
*/
public int size() {
return size;
}
/**
* 如果此映射将一个或多个键映射到指定值,则返回 true。
*/
public boolean contains(Object o) {
return containsValue(o);
}
/**
* 从此映射中移除所有映射关系。
*/
public void clear() {
HashMap.this.clear();
}
}
/**
* 返回此映射所包含的映射关系的 Set 视图。 该 set 受映射支持,所以对映射的更改将反映在此 set 中,反之亦然。
* 如果在对 set 进行迭代的同时修改了映射(通过迭代器自己的 remove 操作,
* 或者通过在该迭代器返回的映射项上执行 setValue 操作除外),则迭代结果是不确定的。
* 该 set 支持元素的移除,通过 Iterator.remove、Set.remove、removeAll、retainAll
* 和 clear 操作可从该映射中移除相应的映射关系。它不支持 add 或 addAll 操作。
* 覆写:接口 Map<K,V> 中的 entrySet
* 覆写:类 AbstractMap<K,V> 中的 entrySet
* @return 此映射所包含的映射关系的 set 视图。
*/
public Set<Map.Entry<K,V>> entrySet() {
return entrySet0();
}
/**
* 返回此映射所包含的映射关系的 Set 视图。
* @return 此映射所包含的映射关系的 set 视图。
*/
private Set<Map.Entry<K,V>> entrySet0() {
Set<Map.Entry<K,V>> es = entrySet;
return es != null ? es : (entrySet = new EntrySet());
}
/**
* 此映射所包含的映射关系的 Set 视图
*/
private final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
/**
* 条目迭代器
*/
public Iterator<Map.Entry<K,V>> iterator() {
return newEntryIterator();
}
/**
* 如果有此类目,则返回 true。
*/
public boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<K,V> e = (Map.Entry<K,V>) o;
Entry<K,V> candidate = getEntry(e.getKey()); // 返回与HashMap中指定键(e.getKey())关联的项。
return candidate != null && candidate.equals(e);
}
/**
* 移除EntrySet
*/
public boolean remove(Object o) {
return removeMapping(o) != null;
}
/**
* 返回此映射中包含的键值映射的数量。
*/
public int size() {
return size;
}
/**
* 从此映射中移除所有映射关系。
*/
public void clear() {
HashMap.this.clear();
}
}
/**
* 将HashMap实例的状态保存到流(即,序列化)。
* @serialData HashMap的容量 (bucket数组的长度)被发送(int),
* 然后是size(一个int,键值映射的数量),然后是每个键值映射的键(对象)和值(对象)。
* 键值映射没有特定的顺序发出(emitted)。
* @param s
* @throws IOException
*/
private void writeObject(java.io.ObjectOutputStream s)
throws IOException
{
Iterator<Map.Entry<K,V>> i =
(size > 0) ? entrySet0().iterator() : null;
// 写出阈值(threshold)、负载因子(loadfactor)和任何隐藏的东西
s.defaultWriteObject();
//写出桶的数量
s.writeInt(table.length);
//写出大小(size)(映射数number of Mappings)
s.writeInt(size);
//写出键和值(交替)
if (size > 0) {
for(Map.Entry<K,V> e : entrySet0()) {
s.writeObject(e.getKey());
s.writeObject(e.getValue());
}
}
}
private static final long serialVersionUID = 362498820763181265L;
/**
* 从流中重新构造HashMap实例。,反序列化。
* @param s
* @throws IOException
* @throws ClassNotFoundException
*/
private void readObject(java.io.ObjectInputStream s)
throws IOException, ClassNotFoundException
{
// 读取阈值(threshold)(忽略)、负载因子(loadfactor)和任何隐藏的内容
s.defaultReadObject();
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new InvalidObjectException("Illegal load factor: " +
loadFactor);
// 设置hashSeed(只能在VM启动之后进行)
Holder.UNSAFE.putIntVolatile(this, Holder.HASHSEED_OFFSET,
sun.misc.Hashing.randomHashSeed(this));
// 读取桶数(buckets )并分配桶数组;
s.readInt(); // 忽略
// 读取映射数
int mappings = s.readInt();
if (mappings < 0)
throw new InvalidObjectException("Illegal mappings count: " +
mappings);
int initialCapacity = (int) Math.min(
// 根据映射数量和所需负载选择的容量(capacity)(如果>= 0.25)
mappings * Math.min(1 / loadFactor, 4.0f),
// 我们有限制……
HashMap.MAXIMUM_CAPACITY);
int capacity = 1;
// 找出包含所有映射的2的最小幂
while (capacity < initialCapacity) {
capacity <<= 1;
}
table = new Entry[capacity];
threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
useAltHashing = sun.misc.VM.isBooted() &&
(capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
init(); // 给子类一个机会去做它的事情。
// 读取键和值,并将映射放入HashMap中
for (int i=0; i<mappings; i++) {
K key = (K) s.readObject();
V value = (V) s.readObject();
putForCreate(key, value);
}
}
// 这些方法用于序列化哈希集(HashSets)
int capacity() { return table.length; }
float loadFactor() { return loadFactor; }
}
参考:JDK 1.6 API