HashMap是我们在编程中最常用的map,也是面试中经常考的问题,所以打算深入研究一下hashmap的源码,并且对比7和8中的不同。
一、hashmap的数据结构
hashmap的数据结构是哈希表,核心是基于哈希值的桶,而哈希桶的底层实现其实是数组,数组这种数据结构查找的时间复杂度是O(1),所以哈希表的查找、删除、插入的平均时间复杂度就是O(1),但是它也有一个致命的缺陷—哈希碰撞(collision),所谓碰撞,举例说明:两个数据映射出来的哈希值相同,就会发生哈希碰撞(冲突)。下面我们来看下java7和java8分别是怎么解决碰撞问题的。
二、Java 7 HashMap(不是线程安全的)
经典的哈希表的实现—数组+链表,即如果put到的位置有值了,如果key不一致,则会将新put进来的数据放到这个位置原有值的后面,形成链表。画了个图简单展示下:
先来看几个重要的常量,简单的介绍我已经写在了下面的注释中了,其实就是把他给的英文注释简单翻译一下,他给的英文注释很清晰明了了
/**
* The default initial capacity - MUST be a power of two.
* 默认的初始化容量,必须是2的幂次方,默认容量是16(2^4)
* 疑问一:为什么必须是2的幂次方,这样赋值的目的是什么?
*/
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
/**
* The maximum capacity, used if a higher value is implicitly specified
* by either of the constructors with arguments.
* MUST be a power of two <= 1<<30.
* 最大容量,这个值我们一般用不上
*/
static final int MAXIMUM_CAPACITY = 1 << 30;
/**
* The load factor used when none specified in constructor.
* 加载因子,默认0.75,加载因子用来判断什么时候需要对hashmap进行resize()
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;
这里有个疑问,就是为什么初始化容量一定是2^n,这个我们先留着,稍后进行解答。
下面我们先去看下他的默认构造函数HashMap()
/**
* Constructs an empty <tt>HashMap</tt> with the default initial capacity
* (16) and the default load factor (0.75).
* 默认的构造函数,初始容量为16,加载因子为0.75
*/
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
当然,还有可调初始容量和加载因子的构造函数
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);
}
/**
* Inflates the table
*/
private vois inflateTable(int toSize) {
iint capacity=roundUpToPowerOf2(toSize);
...//实际开辟空间存储元素
}
这里有一个需要注意的地方,当你输入的initialCapacity不是2^n,roundUpToPowerOf2(param)方法会自动将你输入的值调整为离他最大的 2的幂次方。这里说一句,并不是调用了构造函数的时候就会开辟空间,而是等到调用put方法时才会真正开辟空间存储元素(默认16)。
如何确定这个obj应该对应哪个哈希桶的索引呢?
hashcode的值有-2^(31) ~2^(31)-1,约有42亿个数,默认的哈希桶的容