先强调一点,Map不继承Collection集合类。
HashMap,key和value可以为空,非线程安全,对应线程安全的类HashTable。
类继承关系:
extends AbstractMap<K,V> implements Map<K,V>,Cloneable,Serializable
//默认初始承载力,2的幂数,承载力就是hash的桶数
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
;
//空数组
static final Entry<?
,?>[]
EMPTY_TABLE = {}
;
//数据数组
transient Entry<
K
,
V>[]
table = (Entry<
K
,
V>[])
EMPTY_TABLE
;
//key-value个数
transient int
size
;
//扩容阈值,此值=承载力*装载因子,当size大于threshold时,进行resize操作。
int
threshold
;
//装载因子
final float
loadFactor
;
//结构被改变次数
transient int
modCount
;
//
容量阈值,默认大小为Integer.MAX_VALUE
static final int
ALTERNATIVE_HASHING_THRESHOLD_DEFAULT = Integer.
MAX_VALUE
;
/
/
计算hash值的时候需要用到
transient int
hashSeed =
0
;
//元素集合
private transient Set<Map.Entry<
K
,
V>>
entrySet =
null;
//初始化hashSeed
final boolean initHashSeedAsNeeded(intcapacity) {
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;
}
//生成hash值
//一次散列,调用k的hashCode方法,与hashSeed做异或操作
//然后进行二次散列
final int hash(Object k) {
inth =hashSeed;
if(0!= h && kinstanceofString) {
returnsun.misc.Hashing.stringHash32((String) k);
}
h ^= k.hashCode();
// This function ensures that hashCodes that differ only by
// constant multiples at each bit position have a bounded
// number of collisions (approximately 8 at default load factor).
h ^= (h >>> 20) ^ (h >>>12);
returnh ^ (h >>> 7) ^ (h >>>4);
}
获取value,get方法:
public V get(Object key) {
if(key ==null)
return getForNullKey();
Entry<K,V> entry = getEntry(key);
return null== entry ? null : entry.getValue();
}
贴一下getEntry方法:
final Entry<K,V> getEntry(Object key) {
if(size==0) {
return null;
}
inthash = (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))))
return e;
}
return null;
}
大致过程:获得key值的hash值,通过hash值找到table的index索引值,遍历这条链,找到与key相同的元素。
这里说一下HashMap的结构,其实HashMap就是一个数组,代码用table表示,每个元素其实又是一个链表的首节点。就是常说的链表法,解决hash冲突,相同hash值的元素,加入在对应位置的链表尾部。如图
put方法:
publicV put(K key,V value) {
if(table==EMPTY_TABLE) {
inflateTable(threshold);
}
if(key ==null)
return putForNullKey(value);
int hash = 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.equals(k))) {
V oldValue = e.value;
e.value= value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash,key,value,i);
return null;
}
//扩容table
private void inflateTable(int toSize) {
// Find a power of 2 >= toSize
int capacity = roundUpToPowerOf2(toSize);
threshold = (int) Math.min(capacity * loadFactor,MAXIMUM_CAPACITY + 1);
table = new Entry[capacity];
initHashSeedAsNeeded(capacity);
}
//添加元素
void addEntry(int hash,K key,V value, intbucketIndex) {
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);
}
这里发现了几个点:
1.无参构造函数(我们常用的new HashMap),只是设置了扩容阈值为
DEFAULT_INITIAL_CAPACITY(16),table默认是空数组。当put元素的时候,先进行一次扩容,承载力扩大到32(hash桶数),扩容阈值为32*0.75(默认)=24
2.添加元素,扩容的条件不只是size>=threshold扩容阈值,还需要
bucketIndex索引位置是null,意思就是
bucketIndex位置还没有元素
旧表转新表的代码:
void transfer(Entry[] newTable, booleanrehash) {
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);
}
inti =indexFor(e.hash,newCapacity);
e.next= newTable[i];
newTable[i] = e;
e = next;
}
}
}
每个index上的链表,从表头开始一个一个添加到新表中。皓哥说并发下会出现死循环,具体可以看下皓哥的博客。