首先讲解HashMap的几个要点:
- HashMap是非线程安全的(效率最高);HashTable是线程安全的效率最低;CurrentHashMap是线程安全的,CurrentHashMap 把内部分成若干个segment,每个segment内部是线程安全的(相当于HashTable),各个segment可以并行操作,因此它的性能优于HashTable 但是 弱于 HashMap。
- 影响HashMap性能的3个关键参数,Capacity 、 loadFacor 和 threshold。Capacity为HashMap的size,loadFacor 为承载因子,threshold为承载界值。当map插入数据个数超过threshold的时候,则需要对HashMap进行动态扩容,扩容之后会对之前的插入的数据的位置进行重新计算和移动,因此每次扩容消耗都非常大,指定合理的 caption 和 loadFacor 对于HashMap的性能提升至关重要。
源码分析:
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY) {
initialCapacity = MAXIMUM_CAPACITY;
} else if (initialCapacity < DEFAULT_INITIAL_CAPACITY) {
initialCapacity = DEFAULT_INITIAL_CAPACITY;
}
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
// Android-Note: We always use the default load factor of 0.75f.
// This might appear wrong but it's just awkward design. We always call
// inflateTable() when table == EMPTY_TABLE. That method will take "threshold"
// to mean "capacity" and then replace it with the real threshold (i.e, multiplied with
// the load factor).
threshold = initialCapacity;
init();
}
private void inflateTable(int toSize) {
// Find a power of 2 >= toSize
int capacity = roundUpToPowerOf2(toSize);
// Android-changed: Replace usage of Math.min() here because this method is
// called from the <clinit> of runtime, at which point the native libraries
// needed by Float.* might not be loaded.
**//capacity * loadFactor决定了临界值,如果hashmap的数量达到了临界值,则需要扩容一倍,**
**//并且之前插入进来的数据需要重新计算位置,然后移动**
float thresholdFloat = capacity * loadFactor;
if (thresholdFloat > MAXIMUM_CAPACITY + 1) {
thresholdFloat = MAXIMUM_CAPACITY + 1;
}
threshold = (int) thresholdFloat;
table = new HashMapEntry[capacity];
}
- HashMap就是一个散列表,其内部结构如下:
对于hashmap的内部结构分析如下:
- HashMap内部结构是通过array和list 共同构成。
- hashmap插入数据过程是 通过 hashcode来得到hash值,然后对hash值进行处理得到一个插入位置buckedindex,后续过程会分三种情况:
a、如果通过哈希值计算出来的位置buckedIndex没有元素,则直接插入。
b、如果buckedIndex存在即是hashcode存在,则比较 插入值的key 和 存在的key是否相等,即是equals方法的使用,如果相等,则用新值替换掉旧值。
c、如果buckedIndex存在即是hashcode存在,则比较 插入值的key 和 存在的key是否相等,如果不相等,则将本数据插入到已存在数据链表的最前面,并且next指针指向后一个数据。
根据b、c 2种情况的介绍,可以知道,如果hashcode相等,就是所谓hash散列碰撞了,碰撞之后则要采用equals方法 依次 比较碰撞位置的各个数据,如果存在key相等则直接更新该此值,如果不存在,则需要插入到本链表的最前面,并且next指针执行之前链表的第一个值。因为发生碰撞会进行各种equals,这里也是影响性能的一个方面,因此尽量减少碰撞的几率,会提高HashMap的性能。
以上是HashMap实现原理,下一个博文会详解LinkedHashMap,LinkedHashMap为什么能保存插入的顺序和实现LRU(最近最少访问),在LinkedHashMap详解中会进行详细描述,并且会解析LruCache实现原理。