hash表是一种数据结构,而hashmap的底层是hash表的 实现,不是hash表
1.hash表 是数组 不是数组加链表,数组加链表是hashmap为了解决hash冲突的hash表的具体实现
2.hash表 是根据 hash算法计算hash值,然后通过散列函数 计算出 数组下标
3.所以,在设计hashcode()和 equals()要保证,equals相同 hashcode必须相同,但是equasl不同 不能代表hash不同
4.hash冲突的原因:得到的数组下标相同,得经过比较才能得出 他们equals相同与否,
因为即使equals不同,得出的hash值不同,但是得到的数组下标可能相同
5.为什么不直接比较equals(直接比就可以判断是否相同),而是先比较hash值
因为hash值已经计算出来了,比较更快,如果hash值不同那么,equals一定不相同,如果hash值相同,再比较equals
6.hashmap的key和value,是存储在entry里面的,entry是一个类里面的属性是key和value
7.JDK 8版本发布以后:HashMap是数组+链表+红黑树实现
解决的问题:数组长度太小没有扩容而链表太长导致的查询速度变慢
终究是一个 数组大小和链表长度的比例问题
8.7.HashMap的扩容
1.为什么要进行扩容
不是数组大小不够了,是数组的桶被占的越来越多,出现hash冲突的次数越来越多,
导致查询速率越来越小
2.扩容为什么是性能消耗最大的点:
原数组中的数据必须重新计算其在新数组中的位置,并放进去,这就是resize
3.扩容是越大越好吗
不是,当达到64了就得考虑是否把链表中的元素转换成红黑数了,是8就转
因为扩容越大,性能消耗越大
4.hashmap什么时候进行扩容
(1.8前后都是这样) 1.当数组元素达到 默认数组长度(一般为16)*0.75 = 12时 ,扩容一倍
然后重新计算每个元素在数组中的位置,而这是一个非常消耗性能的操作
所以如果我们已经预知HashMap中元素的个数,那么预设元素的个数能够有效的提高HashMap的性能
5.1.8之后什么时候链表树形化
1.为什么要转红黑树
1.红黑树查找的时间复杂度是 O(logn) ;而链表查找元素的时间复杂度为 O(n),远远大于红黑树的 O(logn),
尤其是在节点越来越多的情况下,O(logn) 体现出的优势会更加明显;简而言之就是为了提升查询的效率。
2.为什么不一开始就用红黑树
1.TreeNode 需要占用的空间大约是普通 Node 的两倍(树结构结点最少都有2个指针)
由 TREEIFY_THRESHOLD 的值(默认值8)决定的
2.当桶中节点数由于移除或者 resize 变少后,又会变回普通的链表的形式,
以便节省空间,这个阈值是 UNTREEIFY_THRESHOLD(默认值6)。
3.转换阈值 8 是怎么来的
1.反正体现了空间和时间的平衡,一般情况下由于 hash 计算的结果离散好的话,
那么红黑树这种形式是很少会被用到的,因为各个值都均匀分布,很少出现链表很长的情况。
千万分之一的概率
4.什么时候转红黑数
1.当HashMap中的其中一个链的对象个数如果达到了8个,此时如果capacity没有达到64,
那么HashMap会先扩容解决,如果已经达到了64,那么这个链会变成树,结点类型由Node变成TreeNode类型
2.因为:数组没有达到64之前,可以通过扩容,达到效率提升,减少哈希冲突,而且扩容后,
出现哈希冲突的几率变低,那么链表的长度就不会再增,但是扩容的大小>=64了,就不会扩容了
因为再扩容,重新计算其在新数组中的位置,并放进去的消耗太大