一. HashMap与HashSet的底层实现
HashMap在Jdk1.8之前是数组+链表,在1.8之后是数组+链表/红黑树
HashSet的底层就是HashMap
二. HashMap的put方法的底层原理
1.根据key的hashCode计算出数组index
2.落槽时
(1).如果数组中的节点为null时,会创建新的节点对象,把K,V内的数据储存在节点对象中, 然后再将节点对象存储在数组中
(2).如果数组中的节点不为null时,判断该节点对象与添加元素是否会发生哈希冲突,若发生哈希冲突则判断添加对象的key中元素是否与节点元素相同,相同则用新的k,v覆盖原来节点的k,v;不同则以当前节点生成下一个节点储存添加元素的k,v,并在next中存储下新节点的地址,与新节点成为单项链表。在有元素不断的加入同一链表的时,当该链表长度大于阈值8则会将落在该槽下的链表转换为红黑树。
(3)最后判断++size是否大于阈值,是就扩容。
三.HashMap的resize()扩容方法的底层原理
HashMap的默认初始容量是16
resize()方法是在HashMap中的size大于阈值时或者初始化时,就调用resize方法进行扩容
每次扩容的时候始终是原数组长度的2倍,即长度永远是2的n次方
扩容后节点对象的位置要么在原位置,要么偏移到两倍的位置
四.HashMap的长度为什么是2的幂次方
为了能让HashMap存放高效,尽量较少碰撞,也就是要尽量把数据分配均匀,每个链表/红黑树长度大致相同。这个实现就是把数据存到哪个链表/红黑树中的算法。
五.什么是哈希函数和哈希码
-
Hash函数译为哈希函数,又称散列函数。是把任意长度的输入,通过散列算法,变换成固定长度的输出,该输出的值称为散列值或消息摘要。简单来说就是一种将任意长度的输入消息压缩成某一固定长度的消息摘要的函数
-
是把任意长度的输入通过哈希算法变换成固定长度的输出,
哈希函数的输出就是哈希值
六.什么是哈希碰撞/哈希冲突,怎么解决哈希冲突,HashMap采用的是什么策略
哈希冲突就是两个不同值的东西,通过哈希函数计算出来的哈希值相同,这样他们存在数组中的时候就会发生冲突,这就是哈希冲突
解决哈希冲突
1.开放地址法
2.拉链法(链地址法)HashMap默认的方法
把具有相同散列地址的关键字(同义词)值放在同一个单链表中,称为同义词链表。有m个散列地址就有m个链表,同时用指针数组T[0..m-1]存放各个链表的头指针,凡是散列地址为i的记录都以结点方式插入到以T[i]为指针的单链表中。T中各分量的初值应为空指针。
七.HashMap和TreeMap的区别
HashMap通过hashcode对其内容进行快速查找(哈希表通过把关键码值映射到表中一个位置来访问记录,不需比较便可直接取得所查记录,加快了查找的速度)。
而 TreeMap中所有的元素都保持着某种固定的顺序,如果你需要得到一个有序的结果你就应该使用TreeMap(HashMap中元素的排列顺序是不固定的)。
八.如何决定使用HashMap还是TreeMap?
对于在Map中插入、删除和定位元素这类操作,HashMap是最好的选择。然而,假如你需要对一个有序的key集合进行遍历,TreeMap是更好的选择。基于你的collection的大小,也许向HashMap中添加元素会更快,得map换为TreeMap进行有序key的遍历
九.HashMap的遍历方法
1.forEach循环
map.forEach((key,value)->{
System.out.println(key+":"+value);
});
每次取出一组键值对,并把key和value分别赋给箭头函数的两个参数
2.迭代器
Set<Map.Entry<String,Integer>>set =map.entrySet();
//获取Entry集合的迭代对象
Iterator<Map.Entry<String,Integer>>iterator =set.iterator();
//遍历迭代对象
while (iterator.hasNext()){
//取出每个迭代对象
Map.Entry<String,Integer>entry =iterator.next();
//entry.getKey()取出key,entry.getValue()取出Value
System.out.println(entry.getKey()+":"+entry.getValue());
}
3.先遍历Key的集合,再用Key去取Value
//获取Key的集合
Set<String>keySet = map.keySet();
//获取key集合的迭代器
Iterator<String> keyIterator = keySet.iterator();
//遍历迭代器
while (keyIterator.hasNext()){
//取出迭代器
String key = keyIterator.next();
//根据key取出value
System.out.println(key+":"+map.get(key));
}