问题1: HashMap的内部实现?(这边经常会与数组或者链表的比较。)
Java中数据存储方式最底层的两种结构,一种是数组,另一种就是链表。
数组的特点:连续空间,寻址迅速,但是在删除或者添加元素的时候需要有较大幅度的移动,所以查询速度快,增删较慢。
链表的特点:由于空间不连续,寻址困难,增删元素只需修改指针,所以查询慢、增删快。
HashMap的实现主要用到了哈希表的链地址法。即使用数组+链表的方式实现。
问题2:HashMap的效率(经常会跟数组与链表比较)
3.问题3:HashMap中的问题3:key的如果是个Object要实现哪些方法?为什么?
-
hashCode:hashCode方法用来计算在数组中的哪一个位置
-
equals:equals方法用来比较数组展开的链表的元素是否相等。
问题4:hashMap是线程安全的么?
不是,所以需用使用者加锁处理。hashTable跟concurrentHashMap是线程安全的。因为这个问题,hashMap的效率更高。
问题5:什么叫做hash碰撞?
指的是两个不同的key得到同一个hashcode的值。
问题6:如果hashCode方法实现的太挫,会发生什么情况?
如果hashCode方法实现不当,hashMap会退化成链表。
问题7:treeMap与hashMap的区别?
treeMap的底层是红黑树,hashMap的底层实现是哈希表,两者理论时空复杂度都差不多,在理想情况下,但数据较为稀疏时,应尽量选择hashMap,反之选treemap.(同时也应考虑具体的需求问题,抛开实际问题讨论数据结构没啥意义。)
问题8:JDK8是如何解决问题6的问题。
JDK8对hashMap做了优化,当一个散列表里面的元素过多,会将链表转为红黑树。
问题9:什么是hashMap的扩容?
如果当表中的75%已经被占用,即视为需要扩容了。不然效率会下降得比较快(毕竟是链表)。扩容的过程为:
-
容量加倍
-
重新计算所有元素hashCode后重新插入
这里注意到,扩容是需要遍历整个表的。并且这个过程是非线程安全的。
10.优化
-
解决扩容损失:如果知道大致需要的容量,把初始容量设置好以解决扩容损失,比如我现在有1000个数据,需要 1000/0.75 = 1333 ,又 1024 < 1333 < 2048,所以最好使用2048作为初始容量
-
解决碰撞损失:使用高效的HashCode。
-
终极大招。。。。解决数据结构选择的错误:在大型的数据与搜索中考虑使用别的结构比如TreeMap,这个就是积累了,一般需要key排序时,建议使用TreeMap;