在讲hash map之前先简单的讲讲hashcode。hash意思为散列,它能将任意长度的输入,通过散列算法的计算,输出固定长度,该输出就为散列值(hashcode)。
为什么会需要hashcode呢?可以想象我们需要将不同的元素存放入集合,为了快速查找与区分它们,可以使用hash算法将各个元素映射成为固定长度的hashcode,如:王--1、而--2、你--3……这样,根据不同的hashcode将元素存到该数组下标为hashcode值的位置,在需要查找该元素时,只需将元素进行hash算法,得到hashcode之后,用该hashcode作为下标去数组中取元素就可以了。
接下来说说HashMap,它不是继承自Collection容器,它是基于Map接口实现的。在Map中存放的是key—value数据,key是通过hash算法后形成的hashcode,value为我们需要存放的数据,通过key能找到我们所需的value。但是hash算法也不是万能的,因为在数据量非常大的时候,很有可能两个不同的key在通过hash算法时会产生相同的hashcode,这就是hash冲突,为了解决这个问题,HashMap底层基于数组与链表来实现,下面是我从网上找的图:
数组的每一个元素为一个链表,若hashcode相同,则将数据依次存入链表中。下面,我们来看一下HashMap的源码。
1. HashMap的定义
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable
可以看到HashMap是继承自AbstractMap的,它实现了Map、Cloneable、Serializable接口。
2. HashMap的属性
//table为存放key的数组,可以看到节点类型为Node
transient Node<K,V>[] table;
//定义变量entrySet,类型为一个Set,代表着一个Map实体(key-value对)。
transient Set<Map.Entry<K,V>> entrySet;
//HashMap的容量
transient int size;
//操作数
transient int modCount;
//容量的临界值,当实际容器的容量大于(总容量*扩容比率)时进行扩容
int threshold;
//扩容比率
final float loadFactor;
//默认的HashMap的初始容量,大小为16。
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
//HashMap的最大容量,2的30次方
static final int MAXIMUM_CAPACITY = 1 << 30;
//这是一个默认的数据容量阈值因子,也就是说,若HashMap中数据数量超过HashMap容量的3/4时,HashMap就会扩容
static final float DEFAULT_LOAD_FACTOR = 0.75f;
//这也是一个阈值,在HashMap的每个数组上的链表的元素数量超过8个时,该链表会变为红黑树,为了提升查找效率。
static final int TREEIFY_THRESHOLD = 8;
//有时候链表中的数据会删除,当链表中数据数量小于6时,该链表会不使用红黑树。
static final int UNTREEIFY_THRESHOLD = 6;
static final int MIN_TREEIFY_CAPACITY = 64;
//Node为HashMap中链表的节点,用于存放数据。
static class Node<K,V> implements Map.Entry<K,V> {
//hashcode
final int hash;
final K key;
V value;
//指向链表的下一个节点。
Node<K,V> next;
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
3.HashMap的构造函数
//设定初始化容量和扩容比率