目录
HashMap的继承体系
1.HashMap实现了Serializable接口,可以被序列化
2.HashMap实现了Cloneable接口,可以被克隆
3.HashMap继承自AbstractMap类,实现了Map接口
什么是Map
1.Map是一个接口,他是key-value的键值对,一个map不能包含重复的key,并且每一个key只能映射一个value;
2.Map接口提供了三个集合视图:key的集合,value的集合,key-value的集合;
3.Map内元素的顺序取决于Iterator的具体实现逻辑,获取集合内的元素实际上是获取一个迭代器,实现对其中元素的遍历;
4.Map接口的具体实现中存在三种Map结构,其中HashMap和TreeMap都允许存在null值,而HashTable的key不允许为空,但是HashMap不能保证遍历元素的顺序,TreeMap能够保证遍历元素的顺序。
哈希表
哈希表(HashTable,散列表)是根据key-value进行访问的数据结构,他是通过把key映射到表中的一个位置来访问记录,加快查找的速度,其中映射的函数叫做散列函数,存放记录的数组叫做散列表,哈希表的主干是数组
上面的图中就是一个值插入哈希表中的过程,那么存在的问题就是不同的值在经过hash函数之后可能会映射到相同的位置上,当插入一个元素时,发现该位置已经被占用,这时候就会产生冲突,也就是所谓的哈希冲突,因此哈希函数的设计就至关重要,一个好的哈希函数希望尽可能的保证计算方法简单,但是元素能够均匀的分布在数组中,但是数组是一块连续的且是固定长度的内存空间,不管一个哈希函数设计的多好,都无法避免得到的地址不会发生冲突,因此就需要对哈希冲突进行解决。
(1)开放定址法:当插入一个元素时,发生冲突,继续检查散列表的其他项,直到找到一个位置来放置这个元素,至于检查的顺序可以自定义;
(2)再散列法:使用多个hash函数,如果一个发生冲突,使用下一个hash函数,直到找到一个位置,这种方法增加了计算的时间;
(3)链地址法:在数组的位置使用链表,将同一个hashCode的元素放在链表中,HashMap就是使用的这种方法,数组+链表的结构。
存储结构
HashMap采用了(数组 + 链表 + 红黑树)的实现结构,数组的一个元素称作为桶。
数组中的每一个元素值为链表,此链表为单向链表,jdk1.8之后使用的是红黑树的存储体系
在添加元素时,会根据key的hash值计算出元素在数组中的位置,如果该位置中没有元素,则直接把元素存储在此位置,如果该位置有元素了,则把元素以链表的形式存放在链表的尾部
HashMap的属性
//默认的初始容量为16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
//最大的容量为2的30次方
static final int MAXIMUM_CAPACITY = 1 << 30;
//默认的装载因子
static final float DEFAULT_LOAD_FACTOR = 0.75f;
//当桶中元素的个数大于8时进行树化
static final int TREEIFY_THRESHOLD = 8;
//当桶中的元素个数小于等于6时转换为链表
static final int UNTREEIFY_THRESHOLD = 6;
//桶的个数达到64个时进行树化
static final int MIN_TREEIFY_CAPACITY = 64;
//数组,元素称为桶
transient Node<K,V>[] table;
transient Set<Map.Entry<K,V>> entrySet;
transient int size;
Node内部类
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;//存储key的hash值
final K key;
V value;
Node<K,V> next;
......
}
-
数组容量:
数组的容量默认为16个,最大为2的30次方 -
装载因子:
装载因子用来计算容量达到多少时进行扩容,默认为0.75 -
树化:
当容量达到64个且链表的长度达到8个时才进行树化,当链表小于6个时才进行反树化
HashMap的构造方法
//1.指定容量和负载因子
public HashMap(int initialCapacity, float loadFactor) {
//判断初始容量是否合法
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
//判断初始容量是否大于最大容量,如果大于则初始化为最大容量 1 << 30
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAP