您的位置:首页 > 编程语言 > Java开发

JDK1.7版本HashMap的源码分析

2020-01-13 08:26 274 查看

注意:HashMap的源码本文仅分析JDK1.7,1.8版本的请参考: JDK1.8版本HashMap

JDK1.7版本HashMap

1 结构图


HashMap由数组(本文后续以table表示)+ 链表构成。其本意是用table来存储一个键值对(本文后续以Entry表示),然后可以通过key取hash值同时对数组长度取模,散列分布到数组每个下标下的时候,那取值时间复杂度将为O(1),在最理想的情况下,就是数组一个下标上只存一个Entry值。
但是天不如人愿,并不存在一个如此优秀的Hash函数,让每个Entry的key一定能散列分布在数组每个下标下,总是会出现如下情况:不同的key值,取hash值并取模后,数组下标值一样,那这个时候就出现了Hash冲突。解决Hash冲突方法有挺多种的,而HashMap采用链表来解决,意思是,假如出现Hash冲突后,则在该下标下,形成一个单链表,存储不同的Entry,那么在查询操作的时候,就需要对链表进行遍历,同时调用HashMap的key的equals方法,判断其是否相等,相等则返回该Entry,这也就是HashMap为什么要求key对象一定要实现hashCode方法和equals方法的原因。

2 继承关系

我们来看下HashMap的继承关系图:

左边实现的Cloneable接口和Serializable接口是为了实现克隆和序列化功能,非本文重点关注对象,请忽略。HashMap主要继承父类AbstractMap,而父类实现了Map接口。

2.1 Map接口

下面是Map接口提供的方法:

可以看到Map还有一个内部接口Entry,Entry就是代表键值对,一个key和一个vale值。

2.2 AbstractMap抽象类(非重点父类,请稍微浏览即可)

AbstractMap对于HashMap的作用,仅仅是提供了keySet 和values 变量。而其他的Map方法都在HashMap中重写了一遍。因此请看淡AbstractMap抽象父类。

AbstractMap.java(省略了其实现的方法):

public V put(K key, V value) {
throw new UnsupportedOperationException();
}
...
public abstract Set<Entry<K,V>> entrySet();

以上两个方法是AbstractMap未实现Map接口的方法。其它方法,AbstractMap基本都实现了。

2.3 HashMap类

HashMap虽然继承了AbstractMap类,但实际上,它基本上重写了AbstractMap类实现Map接口的方法,换句话说,他表面上继承AbstractMap,但暗地里把它实现的方法都重写了一遍,丝毫不留情面。其只使用了父类的两个成员变量keySet和values


2.3.1 字段
/**
* 默认初始化容量
*/
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

/**
* 最大容量值
*/
static final int MAXIMUM_CAPACITY = 1 << 30;

/**
* 默认负载因子
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;

/**
* 空table
*/
static final Entry<?, ?>[] EMPTY_TABLE = {};

/**
* table,用来存放键值对,键值对可以单个值,也可以是一个链表
*/
transient Entry<K, V>[] table = (Entry<K, V>[]) EMPTY_TABLE;

/**
* 大小
*/
transient int size;

/**
* 阈值,扩容的主要判断条件之一
*/
int threshold;

/**
* 负载因子
*/
final float loadFactor;

/**
* 结构更改次数的统计,增、删都会加1。也是快速失败的触发依据,后面HashIterator会介绍如何使用该值
*/
transient int modCount;

/**
* 默认备选hash阈值
*/
static final int ALTERNATIVE_HASHING_THRESHOLD_DEFAULT = Integer.MAX_VALUE;

/**
* 注意、注意、注意:
* 这两个成员变量来自父类AbstractMap,由于其为default类型,在不同包将访问不到,因此在此声明,以此代替
* 由于keySet、value在父类中的keySet()、values()、clone()方法中用到,而HashMap这几个方法全部重写了,因此不会影响使用
*/
transient volatile Set<K> keySet = null;
transient volatile Collection<V> values = null;

2.3.2 构造器

最重要的构造器:

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);
// 初始化负载因子和阈值
// 注意:阈值在这里并非稳定值,在put方法进行初始化后,会等于数组长度*负载因子的值
this.loadFactor = loadFactor;
threshold = initialCapacity;
// init是空实现,忽略
init();
}

接收容量的构造器:

// 接收容量的构造器,并将负载因子设置为默认的0.75
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}

最常用的构造器,无参构造:

// 用得最多的构造器,默认容量16、负载因子0.75
public HashMap() {
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
}

接收一个map的构造器:

public HashMap(Map<? extends K, ? extends V> m) {
this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
inflateTable(threshold);

putAllForCreate(m);
}

2.3.3 get(Object key)方法的实现原理
// 取值
public V get(Object key) {
// 由于HashMap支持一个null值,因为key为null的时候,作特殊处理
// null的值固定在table[0]下
if (key == null)
return getForNullKey();  // 代码1
// 取entry
Entry<K, V> entry = getEntry(key);  // 代码2

return null == entry ? null : entry.getValue();
}

由于HashMap支持key=null,而对于key=null的情况,由代码1进行特殊处理,下面是其对应逻辑:

// 取key=null的值
private V getForNullKey() {
if (size == 0) {
return null;
}
for (Entry<K, V> e = table[0]; e != null; e = e.next) {
if (e.key == null)
return e.value;
}
return null;
}

遍历table[0]下标下的值,由于key=null的特殊值,固定会放在table[0]下。假如table[0]下的结点,有key为null的,则将该值返回。

当key不为null时,由代码2取key对应的Entry,下面看看getEntry的实现逻辑:

// 获取键值对
final Entry<K, V> getEntry(Object key) {
if (size == 0) {
return null;
}

// 计算key的hash值
int hash = (key == null) ? 0 : hash(key);
// 遍历某个下标下的链表(如果是链表的话)
for (Entry<K, V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
// 如果key的hash相同 && (key相同或者key的equals方法的值相同)则返回
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
}
return null;
}

该方法分两步,首先根据key,计算该key对应的下标值。
第二步,遍历下标对应的链表,然后分别判断其hash值是否相等且(key相同或者key的equals方法相等)则将该值返回。

2.3.4 put(K key, V value)方法的实现原理
// 添加值
public V put(K key, V value) {
// 如果table为空,则根据阈值初始化table的值。
// 由于HashMap是延迟初始化的,构造器只给table赋值为EMPTY_TABLE
if (table == EMPTY_TABLE) {
inflateTable(threshold);  // 代码3
}
// 特殊处理key=null的情况
if (key == null)
return putForNullKey(value);	// 代码4
// 计算hash值
int hash = hash(key);	// 代码5
// 根据hash值与table长度,取模(实际为与操作)得到下标值
int i = indexFor(hash, table.length);  // 代码6
// 判断在当前下标下的链表有没有相同key值的Entry,有则覆盖,并将旧值返回
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;
}
}

// 假如没有相同key值,则将为HashMap增加一个新值
// 由于结构发生改变,因此modCount需要+1
modCount++;
// 添加新的Entry值
addEntry(hash, key, value, i);  // 代码7
return null;
}

从上面构造器可以发现,HashMap是延迟初始化的,最开始的时候,给table赋值了一个EMPTY_TABLE,但真正将值设置进来的时候,才去申请capacity对应的空间。
代码3,当table==EMPTY_TABLE的时候,将执行初始化操作,下面代码看看其如何初始化的:

// 初始化table的值
private void inflateTable(int toSize) {
// 将容量控制为2的n次方
int capacity = roundUpToPowerOf2(toSize);

// 阈值载不大于MAXIMUM_CAPACITY+1的情况下,值等于capacity*loadFactor
threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
// 根据容量,创建Entry数组
table = new Entry[capacity];
// 初始化hash种子,可能会对hashSeed字段赋值,hashSeed可能会影响hash(Object key)方法的计算,下面会讲述
initHashSeedAsNeeded(capacity);
}

回到put方法的代码,在完成table值的初始化后,往下执行到代码4,会对key==null的情况进行特殊处理:

// 特殊处理key=null的值,将其放在table[0]下
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++;
// 添加键值对
addEntry(0, null, value, 0);
return null;
}

如果key!=null的时候,计算key对象的hash值,并取模得到key对应table的下标值,下面统一对于代码代码5和代码6进行分析:

代码5:

// 对key值作hash运算
final int hash(Object k) {
// hashSeed在大部分情况下是0,当容量超过2147483647后,可能会变为一个随机hashSeed(该情况不做分析)
int h = hashSeed;
if (0 != h && k instanceof String) {
return sun.misc.Hashing.stringHash32((String) k);
}

// h为0的时候,下面异或操作结果将是 右边操作数的值,则下面相当于h = k.hashCode()
h ^= k.hashCode();

// 以下代码使用了扰动计算,异或了高位+低位的值,避免高位没有参与计算,导致在与table-1做与运算的时候,无法散列在各个下标值下
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}

在hashSeed不为0的时候且key为String类型的时候,可能会调用Hashing.stringHash32进行特殊处理,来的到hash值。
以上情况不满足的时候,将key的hashCode赋值给h,然后通过异或h的多个比特位进行计算。这里用了扰动计算,将hashCode的每个值都用到计算中,以支持散列分布。

代码6:

// 获取当前key下落在table哪个下标,用与运算,等效取模,但性能比取模快
static int indexFor(int h, int length) {
// 首先,length要求为2的n次方,2的n次方-1的时候,得到一个二进制是全1的值
// 当h的值去与操作一个全1的值,等效于对 %length
// 那为什么不用取模操作,而要与操作呢?因为与操作效率比取模操作速度快很多
return h & (length - 1);
}

回到put方法,在计算取得下标值后,将判断当前添加的键值对key是否已存在,开始遍历保存在该下标下的链表,当结点的hash值相等且(key相同或者key的equals方法相等)则覆盖该值,并将旧值返回。
如果以上情况都不满足,则说明当前添加的Entry既不是key==null,又不是key存在的情况。那么开始新增一个键值对,然后返回null。该操作将可能触发扩容。

代码7:

// 添加新的键值对
void addEntry(int hash, K key, V value, int bucketIndex) {
// 判断当前容器大小是否大于等于阈值 && 当前table下标的值存在值,将进行扩容操作
if ((size >= threshold) && (null != table[bucketIndex])) {
// 扩容为当前table长度的两倍
resize(2 * table.length);  // 代码8
// 重新计算扩容后,当前key将位于table的哪个下标
hash = (null != key) ? hash(key) : 0;
bucketIndex = indexFor(hash, table.length);
}
// 创建键值对
createEntry(hash, key, value, bucketIndex);
}

代码8的扩容方法,在下面讲述,将解析其为什么在多线程环境下会形成环路。

2.3.5 resize()方法原理及多线程环境下环路的形成原因
// 扩容方法,多线程不安全,其他方法也是多线程不安全,但是resize问题比较严重
// 可能形成环形链表,造成死循环,CPU100%
void resize(int newCapacity) {
// 暂存旧的table
Entry[] oldTable = table;
// 暂存旧table长度
int oldCapacity = oldTable.length;
// 假如旧的table长度等于最大容量,将不做扩容操作
if (oldCapacity == MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return;
}

// 根据新容量,新建新table
Entry[] newTable = new Entry[newCapacity];

// 执行table上面值得转移,将oldTable上面的值转移到newTable,会重新hash并取模来判断新的下标值
// 多线程环境下,下面方法可能会产生环形链表,造成死循环
transfer(newTable, initHashSeedAsNeeded(newCapacity));
// 将newTable 替换为table
table = newTable;
// 重新计算阈值 ,控制阈值不能超过MAXIMUM_CAPACITY+1
threshold = (int) Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
}

在进行newCapacity(新容量)的合法值校验完成后,创建一个新的Entry数组,然后开始调用transfer进行元素的转移,意思就是把旧数组上面的元素值,进行重新hash并取模,确定其在新数组的下标,我们来看看其如何实现:

// 转移值
void transfer(Entry[] newTable, boolean rehash) {
// 获取新的容量
int newCapacity = newTable.length;
// 遍历table的Entry
for (Entry<K, V> e : table) {
// 遍历链表(如果是链表的话)
while (null != e) {
// 假如线程A取到next,然后时间片用完,CPU切换到线程B继续执行链表值转移操作,并执行完毕
// 那么线程A有了CPU时间片后,继续执行,此时再触发转移值操作,很容易会形成环形链表,造成死循环
Entry<K, V> next = e.next;
if (rehash) {
e.hash = null == e.key ? 0 : hash(e.key);
}
// 根据hash值取模,得到在新table的下标值
int i = indexFor(e.hash, newCapacity);

// 采用头插法,将当前节点下一个节点指向newTable当前下标的值
e.next = newTable[i];
// 将当前节点作为newTable的头结点
newTable[i] = e;
// 将下一个节点赋值给下一轮循环的结点,继续往下执行循环
e = next;
}
}
}

这里注意一点是,newTable是作为方法参数传递进来,每个线程都会创建一个新的newTable,其值是线程私有,而线程不安全的变量,其实是table里面的Entry。具体如何成环路的场景分析,请参考下面的示例图。

图片地址: https://www.processon.com/diagraming/5e06f27ce4b0125e2924abba

2.3.5 keySet()方法的实现原理
public Set<K> keySet() {
Set<K> ks = keySet;
return (ks != null ? ks : (keySet = new KeySet()));
}

上述代码为keySet实现原理,很简单,就是在keySet为null的时候,new了一个KeySet对象,那下面我们看看KeySet对象:

private final class KeySet extends AbstractSet<K> {
// 实现Set的迭代方法
public Iterator<K> iterator() {
return newKeyIterator();
}
// 大小值跟HashMap大小一致
public int size() {
return size;
}
// 调用HashMap的containsKey方法
public boolean contains(Object o) {
return containsKey(o);
}
// 调用HashMap的removeEntryForKey方法
public boolean remove(Object o) {
return HashMap.this.removeEntryForKey(o) != null;
}
// 调用HashMap的clear方法
public void clear() {
HashMap.this.clear();
}
}

由上述代码看出,其具体实现,全都依赖于HashMap。这里可能有个困惑的点,就是我们看到一个Set对象,就会想当然觉得里面的元素都是存储在Set对象里面,其实不然。KeySet对象的值,如元素的值,集合大小等信息,其实都是用着HashMap的成员域。如果你获取到KeySet对象后,调用了remove方法,实际上会改变该key对应在HashMap中的Entry。

我们分析一下iterator方法:

public Iterator<K> iterator() {
// 	调用迭代器方法时,会创建一个新的Key迭代器
return newKeyIterator();
}

调用HashMap的方法newKeyIterator创建一个KeyIterator对象。

Iterator<K> newKeyIterator()   {
// 每次调用都会创建一个新的KeyIterator
return new KeyIterator();
}

KeyIterator 类:

private final class KeyIterator extends HashIterator<K> {
public K next() {
return nextEntry().getKey();
}
}

其next方法是调用父类HashIterator的nextEntry方法获取一个Entry,然后再获取Entry的key,这跟ValueIterator的实现是对应的。
下面我们看看nextEntry方法的具体实现:

private abstract class HashIterator<E> implements Iterator<E> {
Entry<K,V> next;        // 需要返回的下一个Entry
int expectedModCount;   // 快速失败的判断依据,如果该值不等于HashMap的modCount,将抛出ConcurrentModificationException
int index;              // 当前迭代下标
Entry<K,V> current;     // 当前Entry

HashIterator() {
// 将HashMap结构修改次数保存一个副本,下面迭代的时候,如果两个值不相等,将抛出并发修改异常(其实不太准确,下面案例分析)ConcurrentModificationException
expectedModCount = modCount;
if (size > 0) { // advance to first entry
Entry[] t = table;
// 遍历table,直到定位到table不为null的值
while (index < t.length && (next = t[index++]) == null);
}
}

public final boolean hasNext() {
return next != null;
}

final Entry<K,V> nextEntry() {
// 判断在执行迭代器期间,HashMap有没有对结构进行修改
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;
}

public void remove() {
if (current == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
Object k = current.key;
current = null;
HashMap.this.removeEntryForKey(k);
expectedModCount = modCount;
}
}

从HashIterator的代码我们可以看出,在其成员域中维护了

3 源码

下面是源码,篇幅较长,上文已将重要方法都讲解了,如果没有兴趣,可以不看。篇幅长,所以可能会比较卡!

package hashmap1_7;

import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.Serializable;
import java.util.*;

/**
* JDK1.7版本,加入个人一些理解、注释
* 当使用该类的时候,记得要把项目的JDK版本切换为1.7,否则会报错
* copy form jdk1.7
* @author wzj
* 2019/12/28
*/
public class HashMap<K, V>
extends AbstractMap<K, V>
implements Map<K, V>, Cloneable, Serializable {

/**
* 默认初始化容量
*/
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

/**
* 最大容量值
*/
static final int MAXIMUM_CAPACITY = 1 << 30;

/**
* 默认负载因子
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;

/**
* 空table
*/
static final Entry<?, ?>[] EMPTY_TABLE = {};

/**
* table,用来存放键值对,键值对可以单个值,也可以是一个链表
*/
transient Entry<K, V>[] table = (Entry<K, V>[]) EMPTY_TABLE;

/**
* 大小
*/
transient int size;

/**
* 阈值,扩容的主要判断条件之一
*/
int threshold;

/**
* 负载因子
*/
final float loadFactor;

/**
* 结构更改次数的统计,增、删都会加1。也是快速失败的触发依据,后面HashIterator会介绍如何使用该值
*/
transient int modCount;

/**
* 默认备选hash阈值
*/
static final int ALTERNATIVE_HASHING_THRESHOLD_DEFAULT = Integer.MAX_VALUE;

/**
* 注意、注意、注意:
* 这两个成员变量来自父类AbstractMap,由于其为default类型,在不同包将访问不到,因此在此声明,以此代替
* 由于keySet、value在父类中的keySet()、values()、clone()方法中用到,而HashMap这几个方法全部重写了,因此不会影响使用
*/
transient volatile Set<K> keySet = null;
transient volatile Collection<V> values = null;

private static class Holder {

/**
* 备选hash阈值
*/
static final int ALTERNATIVE_HASHING_THRESHOLD;

static {
// 从系统获取阈值的配置,一般没有配置
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;

// 对阈值合法性进行校验
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;
}
}

/**
* hash种子
*/
transient int hashSeed = 0;

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);
// 初始化负载因子和阈值
// 注意:阈值在这里并非稳定值,在put方法进行初始化后,会等于数组长度*负载因子的值
this.loadFactor = loadFactor;
threshold = initialCapacity;
// init是空实现,忽略
init();
}

// 接收容量的构造器,并将负载因子设置为默认的0.75
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}

// 用得最多的构造器,默认容量16、负载因子0.75
public HashMap() {
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
}

// 接收一个map的构造器
public HashMap(Map<? extends K, ? extends V> m) {
this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
inflateTable(threshold);

putAllForCreate(m);
}

// 根据传入的值,得到一个接近的2的n次方数
private static int roundUpToPowerOf2(int number) {
// 以下操作分三步
// 1、判断是否超过容量最大值,超过则取容量最大值
// 2、判断是否小于最小值,小于则取最小值1
// 3、当上述情况不符合,则说明值在正常范围
// 让该值-1再乘以2,然后调用Integer的highestOneBit方法,向下取一个最近的2的n次方值
// 如:1取1(2^0)、 2-3取2(2^1)、 4-7取4(2^2) 、8-15取8(2^3) 、16-31取16(2^4)
return number >= MAXIMUM_CAPACITY
? MAXIMUM_CAPACITY
: (number > 1) ? Integer.highestOneBit((number - 1) << 1) : 1;
}

// 初始化table的值
private void inflateTable(int toSize) {
// 将容量控制为2的n次方
int capacity = roundUpToPowerOf2(toSize);

// 阈值载不大于MAXIMUM_CAPACITY+1的情况下,值等于capacity*loadFactor
threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
// 根据容量,创建Entry数组
table = new Entry[capacity];
// 初始化hash种子,可能会对hashSeed字段赋值,hashSeed可能会影响hash(Object key)方法的计算,下面会讲述
initHashSeedAsNeeded(capacity);
}

void init() {
}

// 初始化哈希掩码值。我们将初始化推迟到真正需要的时候。
final boolean initHashSeedAsNeeded(int capacity) {
// 第一次进入该方法,currentAltHashing一直为false
boolean currentAltHashing = hashSeed != 0;
// 只有等到容量值超过2147483647,则会触发给hashSeed赋值
// sun.misc.VM.isBooted为虚拟机启动后一直为true && 容量大于最大值
boolean useAltHashing = sun.misc.VM.isBooted() &&
(capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
boolean switching = currentAltHashing ^ useAltHashing;

if (switching) {
// 假如useAltHashing为true,则将hashSeed设置为随机产生的HashSeed
hashSeed = useAltHashing
? sun.misc.Hashing.randomHashSeed(this)
: 0;
}
return switching;
}

// 对key值作hash运算
final int hash(Object k) {
// hashSeed在大部分情况下是0,当容量超过2147483647后,可能会变为一个随机hashSeed(该情况不做分析)
int h = hashSeed;
if (0 != h && k instanceof String) {
return sun.misc.Hashing.stringHash32((String) k);
}

// h为0的时候,下面异或操作结果将是 右边操作数的值,则下面相当于h = k.hashCode()
h ^= k.hashCode();

// 以下代码使用了扰动计算,异或了高位+低位的值,避免高位没有参与计算,导致在与table-1做与运算的时候,无法散列在各个下标值下
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}

// 获取当前key下落在table哪个下标,用与运算,等效取模,但性能比取模快
static int indexFor(int h, int length) {
// 首先,length要求为2的n次方,2的n次方-1的时候,得到一个二进制是全1的值
// 当h的值去与操作一个全1的值,等效于对 %length
// 那为什么不用取模操作,而要与操作呢?因为与操作效率比取模操作速度快很多
return h & (length - 1);
}

public int size() {
return size;
}

public boolean isEmpty() {
return size == 0;
}

// 取值
public V get(Object key) {
// 由于HashMap支持一个null值,因为key为null的时候,作特殊处理
// null的值固定在table[0]下
if (key == null)
return getForNullKey();
// 取entry
Entry<K, V> entry = getEntry(key);

return null == entry ? null : entry.getValue();
}

// 取key=null的值
private V getForNullKey() {
if (size == 0) {
return null;
}
for (Entry<K, V> e = table[0]; e != null; e = e.next) {
if (e.key == null)
return e.value;
}
return null;
}

public boolean containsKey(Object key) {
return getEntry(key) != null;
}

// 获取键值对
final Entry<K, V> getEntry(Object key) {
if (size == 0) {
return null;
}

// 计算key的hash值
int hash = (key == null) ? 0 : hash(key);
// 遍历某个下标下的链表(如果是链表的话)
for (Entry<K, V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
// 如果key相同且值相同,则返回
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
}
return null;
}

// 添加值
public V put(K key, V value) {
// 如果table为空,则根据阈值初始化table的值。
// 由于HashMap是延迟初始化的,构造器只给table赋值为EMPTY_TABLE
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
// 特殊处理key=null的情况
if (key == null)
return putForNullKey(value);
// 计算hash值
int hash = hash(key);
// 根据hash值与table长度,取模(实际为与操作)得到下标值
int i = indexFor(hash, table.length);
// 判断在当前下标下的链表有没有相同key值的Entry,有则覆盖,并将旧值返回
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;
}
}

// 假如没有相同key值,则将为HashMap增加一个新值
// 由于结构发生改变,因此modCount需要+1
modCount++;
// 添加新的Entry值
addEntry(hash, key, value, i);
return null;
}

// 特殊处理key=null的值,将其放在table[0]下
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++;
addEntry(0, null, value, 0);
return null;
}

private void putForCreate(K key, V value) {
int hash = null == key ? 0 : hash(key);
int i = indexFor(hash, table.length);

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)))) {
e.value = value;
return;
}
}

createEntry(hash, key, value, i);
}

private void putAllForCreate(Map<? extends K, ? extends V> m) {
for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
putForCreate(e.getKey(), e.getValue());
}

// 扩容方法,多线程不安全,其他方法也是多线程不安全,但是resize问题比较严重
// 可能形成环形链表,造成死循环,CPU100%
void resize(int newCapacity) {
// 暂存旧的table
Entry[] oldTable = table;
// 暂存旧table长度
int oldCapacity = oldTable.length;
// 假如旧的table长度等于最大容量,将不做扩容操作
if (oldCapacity == MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return;
}

// 根据新容量,新建新table
Entry[] newTable = new Entry[newCapacity];
// 执行table上面值得转移,将oldTable上面的值转移到newTable,会重新hash并取模来判断新的下标值
// 多线程环境下,下面方法可能会产生环形链表,造成死循环
transfer(newTable, initHashSeedAsNeeded(newCapacity));
// 将newTable 替换为table
table = newTable;
// 重新计算阈值 ,控制阈值不能超过MAXIMUM_CAPACITY+1
threshold = (int) Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
}

// 转移值
void transfer(Entry[] newTable, boolean rehash) {
// 获取新的容量
int newCapacity = newTable.length;
// 遍历table的Entry
for (Entry<K, V> e : table) {
// 遍历链表(如果是链表的话)
while (null != e) {
// 假如线程A取到next,然后时间片用完,CPU切换到线程B继续执行链表值转移操作,并执行完毕
// 那么线程A有了CPU时间片后,继续执行,此时再触发转移值操作,很容易会形成环形链表,造成死循环
Entry<K, V> next = e.next;

if (rehash) {
e.hash = null == e.key ? 0 : hash(e.key);
}
// 根据hash值取模,得到在新table的下标值
int i = indexFor(e.hash, newCapacity);
// 采用头插法,将当前节点下一个节点指向newTable当前下标的值
e.next = newTable[i];
// 将当前节点作为newTable的头结点
newTable[i] = e;
// 将下一个节点赋值给下一轮循环的结点,继续往下执行循环
e = next;
}
}
}

public void putAll(Map<? extends K, ? extends V> m) {
int numKeysToBeAdded = m.size();
if (numKeysToBeAdded == 0)
return;

if (table == EMPTY_TABLE) {
inflateTable((int) Math.max(numKeysToBeAdded * loadFactor, threshold));
}

if (numKeysToBeAdded > threshold) {
int targetCapacity = (int) (numKeysToBeAdded / loadFactor + 1);
if (targetCapacity > MAXIMUM_CAPACITY)
targetCapacity = MAXIMUM_CAPACITY;
int newCapacity = table.length;
while (newCapacity < targetCapacity)
newCapacity <<= 1;
if (newCapacity > table.length)
resize(newCapacity);
}

for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
put(e.getKey(), e.getValue());
}

// 移除key方法
public V remove(Object key) {
// 移除key对应的Entry
Entry<K, V> e = removeEntryForKey(key);
return (e == null ? null : e.value);
}

// 移除key对应的Entry
final Entry<K, V> removeEntryForKey(Object key) {
if (size == 0) {
return null;
}
// 根据hash值和table长度取模,计算当前key所在table的下标
int hash = (key == null) ? 0 : hash(key);
int i = indexFor(hash, table.length);
// 获取下标所在头结点
Entry<K, V> prev = table[i];
Entry<K, V> e = prev;

// 遍历链表
while (e != null) {
Entry<K, V> next = e.next;
Object k;
// 假如hash值相等 && key的equals方法也相同,则将执行移除
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) {
// 结构变化自增
modCount++;
// 大小减1
size--;
// 判断当前key是否为头结点,如果是,需要将next做为新的头结点,放在table[i]下
// 为什么说prev==e就是key为头结点呢,因为prev==e只有在第一次判断的时候才会相等,下面的prev=e和e=next会改变其值了
if (prev == e)
table[i] = next;
// 当前key对应的值非头结点,则将当前结点的next赋值给上个结点的next,因为准备删除当前结点
else
prev.next = next;
// 空方法,忽略
e.recordRemoval(this);
return e;
}
// 给下一轮循环的结点赋值,向下遍历结点
prev = e;
e = next;
}

return e;
}

final Entry<K, V> removeMapping(Object o) {
if (size == 0 || !(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);
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)) {
modCount++;
size--;
if (prev == e)
table[i] = next;
else
prev.next = next;
e.recordRemoval(this);
return e;
}
prev = e;
e = next;
}

return e;
}

public void clear() {
modCount++;
Arrays.fill(table, null);
size = 0;
}

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;
}

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;
}

public Object clone() {
HashMap<K, V> result = null;
try {
result = (HashMap<K, V>) super.clone();
} catch (CloneNotSupportedException e) {
// assert false;
}
if (result.table != EMPTY_TABLE) {
result.inflateTable(Math.min(
(int) Math.min(
size * Math.min(1 / loadFactor, 4.0f),
// we have limits...
HashMap.MAXIMUM_CAPACITY),
table.length));
}
result.entrySet = null;
result.modCount = 0;
result.size = 0;
result.init();
result.putAllForCreate(this);

return result;
}

// 键值对,其在维护key和value的同时,还会维护next(下一个Entry)和hash(key的hash值)
static class Entry<K, V> implements Map.Entry<K, V> {
final K key;
V value;
Entry<K, V> next;
int hash;

Entry(int h, K k, V v, Entry<K, V> n) {
value = v;
next = n;
key = k;
hash = h;
}

public final K getKey() {
return key;
}

public final V getValue() {
return value;
}

public final V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}

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;
}

public final int hashCode() {
return Objects.hashCode(getKey()) ^ Objects.hashCode(getValue());
}

public final String toString() {
return getKey() + "=" + getValue();
}

void recordAccess(HashMap<K, V> m) {
}

void recordRemoval(HashMap<K, V> m) {
}
}

// 添加新的键值对
void addEntry(int hash, K key, V value, int bucketIndex) {
// 判断当前容器大小是否大于等于阈值 && 当前table下标的值存在值,将进行扩容操作
if ((size >= threshold) && (null != table[bucketIndex])) {
// 扩容为当前table长度的两倍
resize(2 * table.length);
// 重新计算扩容后,当前key将位于table的哪个下标
hash = (null != key) ? hash(key) : 0;
bucketIndex = indexFor(hash, table.length);
}
// 创建键值对
createEntry(hash, key, value, bucketIndex);
}
// 创建键值对
void createEntry(int hash, K key, V value, int bucketIndex) {
// 获取指定下标的元素,作为新结点的next,然后将新结点作为table[index]的头结点
Entry<K, V> e = table[bucketIndex];
table[bucketIndex] = new Entry<>(hash, key, value, e);
// 大小+1
size++;
}
// Hash迭代器,KeyIterator和ValueIterator的父类
private abstract class HashIterator<E> implements Iterator<E> {
Entry<K, V> next;        // 需要返回的下一个Entry
int expectedModCount;    // 快速失败的判断依据,如果该值不等于HashMap的modCount,将抛出ConcurrentModificationException
int index;               // 当前迭代下标
Entry<K, V> current;     // 当前Entry

HashIterator() {
// 将HashMap结构修改次数保存一个副本,下面迭代的时候,如果两个值不相等
// 将抛出并发修改异常(其实不太准确,下面案例分析)ConcurrentModificationException
expectedModCount = modCount;
if (size > 0) {
// 遍历table,直到定位到table不为null的值
Entry[] t = table;
while (index < t.length && (next = t[index++]) == null) ;
}
}

public final boolean hasNext() {
return next != null;
}

final Entry<K, V> nextEntry() {
// 判断在执行迭代器期间,HashMap有没有对结构进行修改
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;
}

public void remove() {
if (current == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
Object k = current.key;
current = null;
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<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;

// 获取key集合
public Set<K> keySet() {
Set<K> ks = keySet;
return (ks != null ? ks : (keySet = new KeySet()));
}
// key集合类,特点:其成员本身不保存元素值,而是在每次迭代的时候,去取HashMap的table上的值
private final class KeySet extends AbstractSet<K> {
public Iterator<K> iterator() {
return newKeyIterator();
}

public int size() {
return size;
}

public boolean contains(Object o) {
return containsKey(o);
}

public boolean remove(Object o) {
return HashMap.this.removeEntryForKey(o) != null;
}

public void clear() {
HashMap.this.clear();
}
}

// 获取值得集合
public Collection<V> values() {
Collection<V> vs = values;
return (vs != null ? vs : (values = new Values()));
}

// value集合类,特点:其成员本身不保存元素值,而是在每次迭代的时候,去取HashMap的table上的值
private final class Values extends AbstractCollection<V> {
public Iterator<V> iterator() {
return newValueIterator();
}

public int size() {
return size;
}

public boolean contains(Object o) {
return containsValue(o);
}

public void clear() {
HashMap.this.clear();
}
}

public Set<Map.Entry<K, V>> entrySet() {
return entrySet0();
}

private Set<Map.Entry<K, V>> entrySet0() {
Set<Map.Entry<K, V>> es = entrySet;
return es != null ? es : (entrySet = new EntrySet());
}

// 键值对集合类
private final class EntrySet extends AbstractSet<Map.Entry<K, V>> {
public Iterator<Map.Entry<K, V>> iterator() {
return newEntryIterator();
}

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());
return candidate != null && candidate.equals(e);
}

public boolean remove(Object o) {
return removeMapping(o) != null;
}

public int size() {
return size;
}

public void clear() {
HashMap.this.clear();
}
}

// 序列化
private void writeObject(java.io.ObjectOutputStream s)
throws IOException {
// Write out the threshold, loadfactor, and any hidden stuff
s.defaultWriteObject();

// Write out number of buckets
if (table == EMPTY_TABLE) {
s.writeInt(roundUpToPowerOf2(threshold));
} else {
s.writeInt(table.length);
}

// Write out size (number of Mappings)
s.writeInt(size);

// Write out keys and values (alternating)
if (size > 0) {
for (Map.Entry<K, V> e : entrySet0()) {
s.writeObject(e.getKey());
s.writeObject(e.getValue());
}
}
}

private static final long serialVersionUID = 362498820763181265L;

// 反序列化
private void readObject(java.io.ObjectInputStream s)
throws IOException, ClassNotFoundException {
// Read in the threshold (ignored), loadfactor, and any hidden stuff
s.defaultReadObject();
if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
throw new InvalidObjectException("Illegal load factor: " +
loadFactor);
}

// set other fields that need values
table = (Entry<K, V>[]) EMPTY_TABLE;

// Read in number of buckets
s.readInt(); // ignored.

// Read number of mappings
int mappings = s.readInt();
if (mappings < 0)
throw new InvalidObjectException("Illegal mappings count: " +
mappings);

// capacity chosen by number of mappings and desired load (if >= 0.25)
int capacity = (int) Math.min(
mappings * Math.min(1 / loadFactor, 4.0f),
// we have limits...
HashMap.MAXIMUM_CAPACITY);

// allocate the bucket array;
if (mappings > 0) {
inflateTable(capacity);
} else {
threshold = capacity;
}

init();  // Give subclass a chance to do its thing.

// Read the keys and values, and put the mappings in the hashmap1_7.HashMap
for (int i = 0; i < mappings; i++) {
K key = (K) s.readObject();
V value = (V) s.readObject();
putForCreate(key, value);
}
}

// 返回容量
int capacity() {
return table.length;
}

// 返回负载因子
float loadFactor() {
return loadFactor;
}
}

https://gitee.com/lao-dubbo/source-code

  • 点赞
  • 收藏
  • 分享
  • 文章举报
老dubbo 发布了20 篇原创文章 · 获赞 7 · 访问量 380 私信 关注
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: