HashMap
最简单的数据结构是数组和对象(指针)
一切复杂的数据结构都可以基于数组和对象实现
HashMap的底层实现
数组+单向链表/红黑树
HashMap类的签名上有两个泛型,分别是K,V代表两个未知的数据类型
什么是哈希码和哈希函数
哈希函数就是一个方法
同一个字符串使用同样的哈希函数计算出来的哈希码必是一样的;不同的字符串使用同样的哈希函数计算出来的哈希码大概率是不一样的,小概率是一样的。
什么是哈希碰撞/冲突
如果有两个字符串通过同样的哈希算法计算出来的哈希码一样,则称他们发生了哈希碰撞/哈希冲突。
如何解决哈希冲突
开放地址法
拉链法(链地址法)HashMap默认使用的就是这种
当某一个链表的长度超过阈值8的时候,此时链表会转化为红黑树结构
如何保证集合中元素的唯一性
1、比那里比较,性能是硬伤
2、通过hash运算,给字符串生成指纹
数组和链表:有序,不唯一
哈希表:无序,唯一
HashSet类
(无序,唯一)
HashSet的底层是HashMap
存储在HashSet集合中的元素,实际上存储在HashMap中的key值
遍历HashMap的几种方式
不可以用普通for循环,非线性结构不支持用索引号遍历
不可以用增强型for循环
可以forEach循环
可以用迭代器 1、先获取Entry集合,再获取Entry集合的迭代器
可以用迭代器 2、先获取key的集合,再获取key集合的迭代器
遍历HashSet的几种方式
可以增强for循环
可以forEach循环
可以迭代器
HashMap的put方法源码解读
整理
1、HashMap的底层实现,HashSet的底层实现
HashMap在JDK1.8之前是数组+链表,JDK1.8之后是数组+链表/红黑树
HashSet的底层是HashMap
2、HashMap的put方法的底层原理
1、根据key的hashCode计算出数组index
2、落槽时
1、如果数组中节点为null,创建新的节点对象,把k,v存储在节点对象中,把节点对象存储在数组中
2、如果数组的节点不为null,判断节点的key与插入元素的key是否相等
1、相等,直接用新的k,v覆盖原节点中的k,v
2、不相等,判断此时节点是否为红黑树
1、是红黑树,创建红黑树节点对象存储k,v,插入到红黑树中
2、不是红黑树,创建链表节点对象存储k,v,插入到链表中,判断链表长度是否大于阈值8
1、大于阈值8,链表转换为红黑树
3、判断++size 是否大于阈值,是就扩容(扩容2倍)
3、HashMap的resize()扩容方法的底层原理
HashMap默认初始容量是16
resize()方法是在hashmap中的size大于阈值时或者初始化时,就调用resize方法进行扩容
每次扩容的时候时钟是原数组长度的2倍,即长度永远是2的n次方
扩容后节点对象的位置要么在原位置,要么偏移两倍的位置
4、HashMap的长度为什么是2的幂次方
为了能让HashMap存取高效,尽量较少碰撞,也就是尽量把数据分配均匀,每个链表/红黑树长度大致相同。这个实现就是把数据存到哪个链表/红黑树中的算法。
5、什么是哈希函数和哈希码
哈希函数就是一个方法
同一个字符串使用同样的哈希函数计算出来的哈希码必是一样的;不同的字符串使用同样的哈希函数计算出来的哈希码大概率是不一样的,小概率是一样的。
6、什么是哈希碰撞/哈希冲突,怎么解决哈希冲突,HashMap采用的是什么策略
如果有两个字符串通过同样的哈希算法计算出来的哈希码一样,则称他们发生了哈希碰撞/哈希冲突。
开放地址法
拉链法(链地址法)HashMap默认使用的就是这种
7、HashMap和TreeMap的区别
HashMap是通过hashcode()对其内容进行快速查找的;HashMap中的元素是没有顺序的;
TreeMap中所有的元素都是有某一固定顺序的,如果需要得到一个有序的结果,就应该使用TreeMap;
8、如何决定使用HashMap还是TreeMap?
对于在Map中插入、删除和定位元素这类操作,HashMap是最好的选择,假如你需要对一个有序的key集合进行遍历,TreeMap是更好的选择。基于你的collection的大小,也许向HashMap中添加元素会更快,将map换为TreeMap进行有序key的遍历。
9. HashMap和HashSet的常用方法