前言:
相信大家肯定有过此类的问题,有的东西用了半天,到底是干嘛的呢?会用了但是不知道为什么要用它,今天就来说说这个HashMap。
正文:
当我们需要存储数据的时候,我们已知的动态数组有哪些呢?
但是这些数组虽然能够自动扩容,但是必须在初始时刻指定初始容量。而对于那些在编译时无法确定具体的数量即动态增长的数据,就需要用到Java集合类了:ArrayList 和 LinkedList,还有 Vector等等......
对于ArrayList 和 LinkedList,还有 Vector它们都有一些缺点,要么插入删除速度慢、要么就是遍历速度慢。
那么有没有一种插入、删除、遍历都比较不错的集合类呢?于是 HashMap 就出现了。
HashMap 是一个散列表,它存储的是一组键值对(key-value)的集合,并实现快速的查找。
特点:
-
为了实现快速查找,HashMap 选择了数组而不是链表。以利用数组的索引实现 O(1) 复杂度的查找效率。
-
为了利用索引查找,HashMap 引入 Hash 算法, 将 key 映射成数组下标: key -> Index。
-
引入 Hash 算法又导致了 Hash 冲突。为了解决 Hash 冲突,HashMap 采用链地址法,在冲突位置转为使用链表存储。
-
链表存储过多的节点又导致了在链表上节点的查找性能的恶化。为了优化查找性能,HashMap 在链表长度超过 8 之后转而将链表转变成红黑树,以将 O(n) 复杂度的查找效率提升至 O(log n)。
底层实现:
JDK1.8之前:HashMap底层是数组+链表,也就是链表散列。HashMap通过key的hashCode经过扰动函数处理后得到hash值,然后通过(n-1)& hash判断当前元素存放的位置,若果但前位置存在元素的话,就判断该元素与要存入的元素hash值以及key是否相同,如果相同直接覆盖,不相同就通过拉链法解决冲突。
JDK1.8之后:当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。
源码对比:
JDK 1.8
static final int hash(Object key) {
int h;
// key.hashCode():返回散列值也就是hashcode
// ^ :按位异或
// >>>:⽆符号右移,忽略符号位,空位都以0补⻬
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
JDK 1.7
static int hash(int h) {
// This function ensures that hashCodes that differ only by
// constant multiples at each bit position have a bounded
// number of collisions (approximately 8 at default load factor).
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
底层数据结构:
JDK1.7
拉链法:将链表和数组相结合,为了解决Hash冲突问题。也就是说创建一个链表数组,数组中每一格就是一个链表,如果遇到hash冲突就将冲突的值加到链表中即可。
JDK1.8
TreeMap、TreeSet以及JDK1.8之后的HashMap底层都用到了红黑树。红黑树就是为了解决二叉查找树的缺陷,因为二叉查找树在某些情况下会退化成一个线性结构,效率很低。
总结:
HashMap 存在的意义就是实现一种快速的查找并且插入、删除性能都不错的一种 K/V(key/value)数据结构。
推荐阅读:拓展资料
任何的限制,都是从内心开始的!
当你不再忍耐,不再克制,才会真正的成熟!
在做任何事情时,都要有坚定且清晰的目标,还要牢记目标!