HashMap的底层实现,HashSet的底层实现
- HashMap在JDK1.8之前是数组+链表,JDK1.8之后是数组+链表/红黑树
- HashSet的底层是HashMap
HashMap的put方法的底层原理
1.根据key的HashCode计算出数组的索引(index)
2.落槽时
2.1如果数组中节点为null,创建新的节点对象,把k,v存储在节点对象中,把节点对象存储在数组中
2.2如果数组节点不为null,判断节点的key与插入元素的key是否相等
2.2.1相等,直接用新的k,v覆盖原节点中的k,v
2.2.2不相等,判断此时节点是否为红黑树
2.2.2.1是红黑树,创建红黑数节点对象存储k,v,插入到红黑树中
2.2.2.2不是红黑树,创建链表节点对象存储k,v,插入到链表中,判断链表长度是否大于阈值8
2.2.2.2.1大于阈值8,链表转换为红黑树
3、判断++size是否大于阈值,是就扩容
HashMap的resize()扩容方法的底层原理
- HashMap默认初始容量是16
- resize()方法是在hashmap中的size大于阈值时或者初始化时,就调用resize方法进行扩容每次扩容的时候始终是原数组长度的2倍,即长度永远是2的n次方
- 扩容后节点对象的位置要么在原位置,要么偏移到两倍的位置
HashMap 的长度为什么是2的幂次方
为了能让 HashMap存取高效,尽量较少碰撞,也就是要尽量把数据分配均匀,每个链表/红黑树长度大致相同。这个实现就是把数据存到哪个链表/红黑树中的算法
什么是哈希函数和哈希码
哈希函数就是一个方法
同一个字符串使用同样的哈希函数计算出来的哈希码必是一样的
不同的字符串使用同样的哈希函数计算出来的哈希码大概率是不一样的,小概率是一样的
什么是哈希碰撞/哈希冲突
如果有两个不同字符串通过同样的哈希算法计算出来的而哈希码是一样的,则它们发生哈希碰撞,哈希冲突
如何解决哈希冲突
1、开放地址法
2、拉链法(连地址法)HashMap默认使用的就是这种
HashMap
最简单的数据结构是数组和对象(指针)
一切复杂的数据结构都可以基于数组和对象实现
HashMap的底层实现
数组+单向链表/红黑树
读HashMap类
什么是哈希码和哈希函数
哈希函数就是一个方法
同一个字符串使用同样的哈希函数计算出来的哈希码必是一样的
不同的字符串使用同样的哈希函数计算出来的哈希码大概率是不一样的,小概率是一样的
什么是哈希碰撞/哈希冲突
如果有两个不同字符串通过同样的哈希算法计算出来的而哈希码是一样的,则它们发生哈希碰撞,哈希冲突
如何确保集合中元素的唯一性
1、遍历+比较 性能是硬伤
2、通过哈希运算,生成字符串指纹(如值取哈希码后四位在通过哈希code算法计算生成一个较小的int数存放在数组,访问直接通过索引)
如何解决哈希冲突
1、开放地址法
2、拉链法(连地址法)HashMap默认使用的就是这种
当某一个链表的长度超过阈值8的时候,此时列表转为红黑树
哈希表中是根据谁来找数据的
根据key
哈希表的特点
无序且唯一
HashSet类
//需要一个元素唯一
//存键值对的话用HashMap
//存一个关键字Key时用HashSet
无序,唯一
HashSet的底层是HashMap
存储在HashSet集合中的元素,实际上存储在HashMap中的Key值
遍历HashMap的几种方法
不可以用普通for循环,非线性结构不支持用索引号遍历
不可以用增强型for循环
可以用forEach循环
可以用迭代器
1、先获取Entry集合,然后再获取Entry集合的迭代器
2、先获取key集合,然后再获取key集合的迭代器
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class Demo03 {
public static void main(String[] args) {
HashMap<String,Integer> map = new HashMap<>();
map.put("张飞",95);
map.put("关羽",85);
map.put("赵云",97);
map.put("马超",84);
map.put("黄忠",83);
System.out.println("HashMap的遍历方式一:forEach");
map.forEach((key,value) -> {
//每次取出一组键值对,并把key和value分别赋给箭头函数的两个参数
//K和V是哈希表里的一个元素
System.out.println(key + ":" + value);
});
System.out.println("HashMap的遍历方式二:迭代器");
//map.entrySet()返回一个Entry集合,一个Entry就是一组键值对
Set<Map.Entry<String, Integer>> set = map.entrySet();
//获取Entry集合的迭代器对象
Iterator<Map.Entry<String, Integer>> iterator = set.iterator();
//迭代器的hasNext()方法可以判断是否还有下一个元素
while (iterator.hasNext()) {
//取出每个Entry对象
Map.Entry<String, Integer> entry = iterator.next();
//entry.getKey()、entry.getValue()取出value
System.out.println(entry.getKey() + ":" + entry.getValue());
}
System.out.println("HashMap的遍历方式三:先获取key集合,如何遍历key,再用key获取value");
//获取key的集合
Set<String> keySet = map.keySet();
//获取key集合的迭代器
Iterator<String> keyIterator = keySet.iterator();
//遍历迭代器
while (keyIterator.hasNext()) {
//取出每个key
String key = keyIterator.next();
//根据key取出value
System.out.println(key + ":" + map.get(key));
}
}
}
遍历HashSet的几种方式
import java.util.HashSet;
import java.util.Iterator;
public class Demo04 {
public static void main(String[] args) {
HashSet<String> set = new HashSet<>();
set.add("张飞");
set.add("关羽");
set.add("刘备");
set.add("赵云");
set.add("黄忠");
System.out.println("遍历方法一:增强型for循环");
for(String name : set){
System.out.println(name);
}
System.out.println("遍历方式二:forEach");
set.forEach((name) -> {
System.out.println(name);
});
System.out.println("遍历方式三:迭代器");
Iterator<String> setIterator = set.iterator();
while (setIterator.hasNext()) {
String next = setIterator.next();
System.out.println(next);
}
}
}