HashMap源码解析
public class HashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable
HashMap继承抽象类AbstractMap,实现了Map接口。
- HashMap的属性
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; //x<<y=x*2的y次方,x>>y=x/2的y次方 1<<4=16 默认初始容量
static final int MAXIMUM_CAPACITY = 1 << 30; //最大容量,2的30次方
static final float DEFAULT_LOAD_FACTOR = 0.75f; //默认的装载因子,达到75%就扩容
static final Entry<?,?>[] EMPTY_TABLE = {}; //定义个Entry类型的空数组
transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE; //HashMap用来维护内部的数据结构
transient int size; //map中保存键值对的数量
int threshold; //需要调整大小的极限值(容量*装载因子)
final float loadFactor; //装载因子
transient int modCount; //map结构改变的次数
static final int ALTERNATIVE_HASHING_THRESHOLD_DEFAULT = Integer.MAX_VALUE; //threshold的最大值
transient int hashSeed = 0; //计算hash值时用的,默认是0
- 静态内部类
static class Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
Entry<K,V> next; //链表形式存储,指向下一个entry
int hash; //entry的hash值
Entry(int h, K k, V v, Entry<K,V> n) { //初始化
value = v;
next = n;
key = k;
hash = h;
}
public final K getKey() { //获取key
return key;
}
public final V getValue() { //获取value
return value;
}
public final V setValue(V newValue) { //设置新的value,返回旧的value
V oldValue = value;
value = newValue;
return oldValue;
}
public final boolean equals(Object o) { //传入节点与当前节点的key,value比较,都相等则返回true
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() { //根据key和value生成hashCode
return Objects.hashCode(getKey()) ^ Objects.hashCode(getValue()); //^异或运算符,就是说相异为1,相同为0,
} //1 ^ 1 = 0;1 ^ 0 = 1;0 ^ 0 =0
public final String toString() {
return getKey() + "=" + getValue();
}
void recordAccess(HashMap<K,V> m) { //LinkedHashMap中有实现
}
void recordRemoval(HashMap<K,V> m) { //LinkedHashMap中有实现
}
}
private static class Holder {
static final int ALTERNATIVE_HASHING_THRESHOLD; //容量阈值,初始化hashSeed的时候会用到该值
static {
String altThreshold = java.security.AccessController.doPrivileged(
new sun.security.action.GetPropertyAction(
"jdk.map.althashing.threshold")); //获取系统变量jdk.map.althashing.threshold
int threshold;
try {
threshold = (null != altThreshold) //假如系统变量jdk.map.althashing.threshold为null或-1时
? Integer.parseInt(altThreshold)
: ALTERNATIVE_HASHING_THRESHOLD_DEFAULT;
if (threshold == -1) {
threshold = Integer.MAX_VALUE; //将容量阈值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;
}
}
- HashMap的构造方法
public HashMap(int initialCapacity, float loadFactor) { //根据传入容量与装载因子,生成一个空HashMap
if (initialCapacity < 0) //初始容量不能小于0
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY) //初始容量不能大于默认的最大容量
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor)) //装载因子不能小于0,且不能为“NaN” (Not a Number)
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor; //将传入的装载因子赋值给装载因子属性
threshold = initialCapacity; //初始容量作为下一次扩容的容量。
init(); //LinkedHashMap中有实现。
}
public HashMap(int initialCapacity) { // 传入初始容量,装载因子使用默认值,生成一个空hashmap。
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
public HashMap() { //初始容量和负载因子全部使用默认值,生成一个空hashmap。
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
}
public HashMap(Map<? extends K, ? extends V> m) { //根据map对象生成一个hashmap,初始容量与传入的map相关,负载因子使用默认值
this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
inflateTable(threshold); //使用threshold新建一个数组
putAllForCreate(m); //将map添加到数组中去
}
private void putAllForCreate(Map<? extends K, ? extends V> m) { //添加指定map里面的所有键值对
for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
putForCreate(e.getKey(), e.getValue());
}
private void putForCreate(K key, V value) {
int hash = null == key ? 0 : hash(key); //如果key为null,则hash值为0,否则根据key计算hash值
int i = indexFor(hash, table.length); //根据hash值和数组的长度找到:该key所属entry在table中的位置i
for (Entry<K,V> e = table[i]; e != null; e = e.next) { //循环链表,假如key相等则替换value值跳出。
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) {
e.value = value;
return;
}
}
createEntry(hash, key, value, i); //如果没找到,将键值对与他的hash值作为一个entry
} //插入table的指定下标中的链表头中
final int hash(Object k) {
int h = hashSeed;
if (0 != h && k instanceof String) { //如果key是String类型,stringHash32来生成hash值
return sun.misc.Hashing.stringHash32((String) k);
}
h ^= k.hashCode(); //一次散列
h ^= (h >>> 20) ^ (h >>> 12); //二次散列
return h ^ (h >>> 7) ^ (h >>> 4);
}
static int indexFor(int h, int length) {
return h & (length-1); //除模取余,相当于hash % length,
} //经笔者数万次循环测试,&比%运算效率会高很多,次数越多,效率约明显
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++;
}
put
public V put(K key, V value) {
if (table == EMPTY_TABLE) { //如果table为空,则新建数组
inflateTable(threshold);
}
if (key == null) //假如key为null,则循环数组,找到key为null的则替换value
return putForNullKey(value); //否则创建一个数组,放到第0位。
int hash = hash(key); //根据key生成hash值
int i = indexFor(hash, table.length); //根据hash值和数组长度找到找到entry所在数组中的位置
for (Entry<K,V> e = table[i]; e != null; e = e.next) { //如果发现存在key与传入key相等,则替换其value,返回原value
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++;
addEntry(hash, key, value, i); //先判断是否需要扩容,然后添加该键值到指定下标的链表头中
return null;
}
private void inflateTable(int toSize) {
int capacity = roundUpToPowerOf2(toSize); //内部数组的大小必须是2的n次方,所以要找到大于toSize的最小的2的n次方。
threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1); //下次扩容临界值
table = new Entry[capacity];
initHashSeedAsNeeded(capacity); //根据数组长度初始化hashseed
}
private static int roundUpToPowerOf2(int number) { //如传入100,则返回128,即2的6次方
return number >= MAXIMUM_CAPACITY //(num-1)<<1 = (100-1)*2的1次方 = 198
? MAXIMUM_CAPACITY //Integer.highestOneBit(198) return 128 即返回2的6次方
: (number > 1) ? Integer.highestOneBit((number - 1) << 1) : 1; //2的7次方就是256了,大于198了,返回不大于198,最大的2的N次方
}
final boolean initHashSeedAsNeeded(int capacity) {
boolean currentAltHashing = hashSeed != 0;
boolean useAltHashing = sun.misc.VM.isBooted() &&
(capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
boolean switching = currentAltHashing ^ useAltHashing;
if (switching) {
hashSeed = useAltHashing
? sun.misc.Hashing.randomHashSeed(this)
: 0;
}
return switching;
}
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;
}
void addEntry(int hash, K key, V value, int bucketIndex) {
if ((size >= threshold) && (null != table[bucketIndex])) {
resize(2 * table.length);
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) {
Entry<K,V> e = table[bucketIndex];
table[bucketIndex] = new Entry<>(hash, key, value, e);
size++;
}
void resize(int newCapacity) { //扩容,当前数组的2倍大小
Entry[] oldTable = table;
int oldCapacity = oldTable.length;
if (oldCapacity == MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return;
}
Entry[] newTable = new Entry[newCapacity]; //创建个新的数组,大小为原数组的2倍
transfer(newTable, initHashSeedAsNeeded(newCapacity)); //把数据都复制到新数组里去
table = newTable; //新数组的引用直接指向table
threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
}
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) {
e.hash = null == e.key ? 0 : hash(e.key);
}
int i = indexFor(e.hash, newCapacity);
e.next = newTable[i];
newTable[i] = e;
e = next;
}
}
}
eg:
import java.util.HashMap;
public class TestMain {
public static void main(String[] args) {
HashMap map = new HashMap();
map.put("英语", 1);
map.put("日语", 2);
map.put("微积分",3);
map.put("数学", 4);
map.put("语文", 5);
map.put("政治", 6);
map.put("体育", 7);
map.put("音乐", 8);
map.put("绘画", 9);
map.put("历史", 10);
map.put("物理", 11);
map.put("化学", 12);
map.put("生物", 13);
map.put("编程", 14);
map.put("哲学", 15);
map.put("经济", 16);
System.out.println(map.toString());
}
}
这里重点讲下indexFor方法,假设我们元这里有个包含16个元素的HashMap,有这么2个素,map.put("语文",1);map.put("物理",1);
这2个元素的key是语文和物理,通过debug测试,得到这2个key的hash值是语文:1073632,物理:964128, h & (length-1)
语文:1073632&(16-1) = 0,物理964128&(16-1) = 0,2个key得到的下标都是0,即数组的第一个位置,此时即可发生碰撞,产生链表结构。
数据结构
bucket | entry | entry |
---|---|---|
语文 | 物理 | |
null | ||
null | ||
日语 | 绘画 | |
音乐 | ||
编程 | ||
null | ||
null | ||
微积分 | 数学 | |
null | ||
null | ||
null | ||
null | ||
null | ||
政治 | 历史 | |
null | ||
null | ||
null | ||
null | ||
null | ||
英语 | ||
哲学 | ||
经济 | ||
体育 | ||
化学 | ||
null | ||
null | ||
null | ||
null | ||
生物 |
这里还拿语文的key举例,1073632&(16-1)二进制 100000110000111100000^1111 结果就是0,这里拿长度16减1,是为了让二进制的低位尽量是1,假如直接用2的N次方,比如16,二进制就是10000,和key的hash值异或,发生碰撞的机会将会大大加大,这样hashMap效率就很差了。
get
public V get(Object key) {
if (key == null)
return getForNullKey(); //如果key为null,则从table[0]中取value
Entry<K,V> entry = getEntry(key); //先根据key,找到其entry
return null == entry ? null : entry.getValue(); //返回entry节点里的value值
}
private V getForNullKey() {
if (size == 0) {
return null;
}
for (Entry<K,V> e = table[0]; e != null; e = e.next) { //查找table[0]处的链表,如果找到entry的key为null,就返回其value
if (e.key == null)
return e.value;
}
return null;
}
final Entry<K,V> getEntry(Object key) {
if (size == 0) {
return null;
}
int hash = (key == null) ? 0 : hash(key); //key为null,hash值为0,否则计算hash值
for (Entry<K,V> e = table[indexFor(hash, table.length)]; //根据hash值和table长度找到table下标,
e != null; //迭代该下标中的链表里的每一个entry节点
e = e.next) {
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
}
return null;
}
remove
public V remove(Object key) {
Entry<K,V> e = removeEntryForKey(key); //根据key来删除entry节点
return (e == null ? null : e.value);
}
final Entry<K,V> removeEntryForKey(Object key) {
if (size == 0) {
return null;
}
int hash = (key == null) ? 0 : hash(key); //计算key的hash值
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;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) {
modCount++;
size--;
if (prev == e)
table[i] = next;
else
prev.next = next;
e.recordRemoval(this);
return e;
}
prev = e;
e = next;
}
return e;
}
Iteator
private abstract class HashIterator<E> implements Iterator<E> {
Entry<K,V> next; //指向下个节点
int expectedModCount; //fast-fail机制
int index; // 当前table下标
Entry<K,V> current; // 当前entry节点
HashIterator() {
expectedModCount = modCount;
if (size > 0) { // advance to first entry
Entry[] t = table;
while (index < t.length && (next = t[index++]) == null)
;
}
}
public final boolean hasNext() {
return next != null;
}
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;
}
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;
}
}
总结
HashMap由数组和链表组成,以key-value存在。key和value都允许为空,key重复会覆盖,value可以重复,无序,非线程安全。
hashmap中查找一个值,需要两次定位,先找到元素在数组的位置的链表上,然后在链表上查找,在HashMap中的第一次定位是由hash值确定的,第二次定位由key和hash值确定。