HashMap介绍
HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。
HashMap 继承于AbstractMap,实现了Map、Cloneable、java.io.Serializable接口。
HashMap 的实现不是同步的,这意味着它不是线程安全的。它的key、value都可以为null。此外,HashMap中的映射不是有序的(后面会讲到)。
简单讲就是:影响HashMap的性能就是初始容量和加载因子,当数据达到当前容量乘以加载因子之后,机会对HashMap进行两倍的扩容(rehash),默认的加载因子是0.75,如果加载因子过大,HashMap数据过多,导入查询插入会变慢,所以当设置初始容量的时候需要考虑好数据,减少rehash
构造函数(基于JDK1.8)
public HashMap(int initialCapacity, float loadFactor) {
//如果初始容量小于零会抛出异常
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
//如果初始容量>最大容量值 初始容量会默认等会最大值 MAXIMUM_CAPACITY = 1 << 30
if (initialCapacity > MAXIMUM_CAPACITY) {
initialCapacity = MAXIMUM_CAPACITY;
} else if (initialCapacity < DEFAULT_INITIAL_CAPACITY) {
//如果初始容量<默认值 复制给最小默认值 DEFAULT_INITIAL_CAPACITY = 4;
initialCapacity = DEFAULT_INITIAL_CAPACITY;
}
//判断加载因子是不是<0的非数字
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();
}
//init为空方法
/**
* Initialization hook for subclasses. This method is called
* in all constructors and pseudo-constructors (clone, readObject)
* after HashMap has been initialized but before any entries have
* been inserted. (In the absence of this method, readObject would
* require explicit knowledge of subclasses.)
*/
void init() {
}
继承关系
public class HashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable
HashMap的api
void clear()
Object clone()
boolean containsKey(Object key)
boolean containsValue(Object value)
Set<Entry<K, V>> entrySet()
V get(Object key)
boolean isEmpty()
Set<K> keySet()
V put(K key, V value)
void putAll(Map<? extends K, ? extends V> map)
V remove(Object key)
int size()
Collection<V> values()
数据在HashMap中的存储方式
先来个图了解一下
通过源码put方法来解释此图
public V put(K key, V value) {
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
if (key == null)
//如果key==null就添加到table第一个
return putForNullKey(value);
int hash = sun.misc.Hashing.singleWordWangJenkinsHash(key);
//获取当前hash在table中的位置
int i = indexFor(hash, table.length);
//判断当前位置是否有相同的key
for (HashMapEntry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
//为什么要比较e.hash==hash因为hash值相同key不一定相同,key相同hash一定相同
//如果遇到相同key把最新的value复制给e.value,返回oldvalue,可以通过put方法的返回是是否为null来判断key是否相同
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 V putForNullKey(V value) {
for (HashMapEntry<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) {
//如果hashmap数量大于规定的大小就rehash,扩容
if ((size >= threshold) && (null != table[bucketIndex])) {
resize(2 * table.length); //扩容到当前的两倍大小
hash = (null != key) ? sun.misc.Hashing.singleWordWangJenkinsHash(key) : 0;
bucketIndex = indexFor(hash, table.length);
}
createEntry(hash, key, value, bucketIndex);
}
//具体添加新数据的方法
void createEntry(int hash, K key, V value, int bucketIndex) {
HashMapEntry<K,V> e = table[bucketIndex];
table[bucketIndex] = new HashMapEntry<>(hash, key, value, e);
size++;
}
- 新的数据的产生。这是一个非常优雅的设计。系统总是将新的Entry对象添加到bucketIndex处。如果bucketIndex处已经有了对象,那么新添加的Entry对象将指向原有的Entry对象,形成一条Entry链,但是若bucketIndex处没有Entry对象,也就是e==null,那么新添加的Entry对象指向null,也就不会产生Entry链了。
- 随着HashMap中元素的数量越来越多,发生碰撞的概率就越来越大,所产生的链表长度就会越来越长,这样势必会影响HashMap的速度,为了保证HashMap的效率,系统必须要在某个临界点进行扩容处理。该临界点在当HashMap中元素的数量等于table数组长度*加载因子。但是扩容是一个非常耗时的过程,因为它需要重新计算这些数据在新table数组中的位置并进行复制处理。所以如果我们已经预知HashMap中元素的个数,那么预设元素的个数能够有效的提高HashMap的性能。
HashMap如果解决碰撞的
- 有上面提到过通过链表来解决遇到相同hash值的数据此方法叫做”拉链法”
- 再hash法,当遇到相同的hash值再hash
术语解释
- 哈希桶:简单说就是table[0],table[1],table[i]代表的数据
本人通过查询博客,以及自己查看代码来理解的HashMap的实现原理,可能有不妥之处,希望各位小哥,大神给与指正,谢谢了!不喜勿喷