HashMap底层是由数组和链表组成的,它的原理是根据key的hashcode再散列取其hash值,然后通过这个hash值与table长度得到key对应的value应该存在数组的哪个位置,其结构如下图
我们在看下HashMap实现put和get的代码以及新建一个HashMap的代码
HashMap有几个默认参数,DEFAULT_INITIAL_CAPACITY是默认初始容量,值为16,MAXIMUM_CAPACITY为最大容量,值为2^30
DEFAULT_LOAD_FACTOR为默认加载因子,值为0.75
HashMap主要的几个成员变量有数组table,大小size,加载因子loadFactor,操作次数modCount,大小临界值threshold
通常我们新建一个HashMap都是用的new HashMap()这个构造方法,那我们就看一下在HashMap里面具体是怎么初始化的
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR;//取默认加载因子作为新建的HashMap的加载因子
threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);//默认初始容量乘以加载因子得出临界值
table = new Entry[DEFAULT_INITIAL_CAPACITY];//新建一个长度为16的数组
init();//源代码中该方法无具体实现
}
下面我们看一下我们常用的put(key,value)方法的实现(自己写的,整体思路和源代码一致,可能会有一些小的问题)
public V put(K key,V value){
if(key==null){
return putvaluefornul(value);//HashMap中,put的时候,如果key是null,一定是存放在table[0]所在的链表中
}
int hash = hash(key.hashcode());//对key的hashcode再散列,得到在数组中坐标对应的hash
int i = indexFor(hash,table.length);//根据key得到的hash和数组长度得到该key对应的数组下标
for(Entry e = table[i];e!=null;e = e.next){
if((e.hash==hash)&&(e.key==key||e.key.equals(key))){//根据hash和key比较看该key是否已存在
Object old = e.value;
e.value = value;//如果该key存在,则替换value
return old;
}
}
addEntry(hash,key,value,i);//不存在的时候增加
return null;
}
public V putvaluefornul(V value){
for(Entry e = table[0];e!=null;e=e.next){
if(e.key==null){
Object old = e.value;
e.value = value;//如果该key存在,则替换value
return old;
}
}
addEntry(0,null,value,0);//不存在的时候增加
return null;
}
public addEntry(int hash,K key, V value, int index){
Entry e = table[index];
e = new Entry(hash,key,value,e);//Entry的构造方法(int hash,K key,V value,Entry next)
if(size++>=threshold){
.....//这一块是扩大数组容量,通常是扩大0.5倍加1,扩容并不是单纯的扩大数组长度,元素的位置也都变了
}
}
我们再看一下get方法的实现
public V get(K key){
if(key==null){
return getvaluefornull(key);//如果key是null,则去table[0]中查找
}
int hash = hash(key.hashcode());//对key的hashcode再散列,得到在数组中坐标对应的hash
int i = indexFor(hash,table.length);//根据key得到的hash和数组长度得到该key对应的数组下标
for(Entry e = table[i];e!=null;e = e.next){
if((e.hash==hash)&&(e.key==key||e.key.equals(key))){//根据hash和key来查找value
return e.value;
}
}
return null;
}
public V getvaluefornull(K key){
for(Entry e = table[0];e!=null;e = e.next){
if(e.key==null){
return e.value;//根据key来查找对应的value
}
}
return null;
}
因为HashMap不是线程安全的,所以其中有modCount这个字段,可以用计HashMap被修改的次数来判断有几个线程修改了它。