HashMap学习笔记
今天我们对HashMap进行一下学习,首先我们要对HashMap要有一个简单的认知,然后通过对源码的阅读,了解HashMap的实现方式以及应用场景。接下来我们要了解的内容包括:
- 什么是HashMap
- HashMap的数据结构组成
- HashMap源码共读
1:什么是HashMap
HashMap 是一个特殊的哈希表(散列表),其结构可以看成由数组+链表组成(jdk1.8之后又引入了红黑树)。
HashMap 存储的内容是键值对(key-value)映射,允许使用null键和null值,HashMap中的映射不是有序的。
HashMap 继承了AbstractMap类,实现了Map,Cloneable,Serializable接口。
HashMap是非线程安全的,也就是说在多线程的环境下,可能会存在问题。
2:HashMap的数据结构组成
在介绍HashMap数据结构组成之前,我们需要了解两个概念–>哈希表和哈希冲突。
哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
哈希冲突:当我们对哈希表做元素插入操作时,首先要对该元素进行哈希运算,得到一个存储地址,然后要进行插入,可是在插入时发现已经被其他元素占用,这就是哈希冲突。
哈希冲突的解决方案有多种:开放定址法(发生冲突,继续寻找下一块未被占用的存储地址),再散列函数法,链地址法,而HashMap即是采用了链地址法,也就是数组+链表的方式。如下图所示:
从上图我们可以看出HashMap底层实现还是数组,只是每个数组都是一条链。那么hashMap是如何实现这种数据结构的呢,我们一起看一下底层源码的实现方式。
3:HashMap源码
按照读源码的习惯,我们先来看一下HashMap的构造方法,
HashMap共有四个构造方法,如果用户在初始化一个HashMap时没有传入initialCopacity和loadFactor这两个参数(这两个参数分别是最大容量和负载因子),会调用无参的构造方法,在调用无参构造方法时initialCapacity默认为16,loadFactory默认为0.75f。
/**
* 默认构造函数
*/
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
/**
*指定“容量大小”的构造函数
*/
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
/**
*指定“容量大小”和“负载因子”的构造函数
*/
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
this.threshold = tableSizeFor(initialCapacity);
}
/**
* 包含“子Map”的构造函数
*/
public HashMap(Map<? extends K, ? extends V> m) {
this.loadFactor = DEFAULT_LOAD_FACTOR;
putMapEntries(m, false);
}