HashMap部分八股文

头插法和尾插法

在JDK1.7的时候是头插法,而JDK1.8及其以后使用了尾插法:当多线程同时操作的时候,可能会导致死循环。

头插法会导致链表翻转。

JDK1.8:在多线程情况下也有存在数据丢失的情况,但是不会导致死循环。


在JDK1.7的时候,使用了entry来存放数据,然后在1.8以后使用了Node

原因(个人猜测):为了处理哈希碰撞,实现了链表转红黑树,而红黑树有TreeNode和LinkNode两种方式,所以就转化为Node。源码上来看也是Entry的二次封装。


为什么负载因子是0.75

因为随机分布的话,0.75左右数据会很少。


哈希表为什么线程不安全

由于为了提高读写性能,使用了缓存——而不同的线程的缓存对于其他线程是不可见的。

其他线程读取的数据很有可能是已经修改或者作废的老数据。

JDK1.7会出现死循环、数据丢失和数据覆盖的问题,但是JDK1.8还是有数据覆盖的问题。


拉链法

就是使用数组 + 链表的方式来存储数据。也就是哈希表的方式

开放定址 /散列法:先去进行哈希,当出现哈希碰撞的时候进行移位直到找到合适的位置。


cas操作和分段锁

在JDK1.7的时候,使用了分段锁Segment(继承于ReentrantLock)实现多线程并发的CurrentHashMap。

但是在JDK1.8的时候使用了cas操作 + synchronized实现的。

cas操作底层使用了unSafe这个类。会同时记录旧值和新值。当旧值被修改的时候会发生自旋。

  • 都是多把锁,每个锁之间是可以并行的,但是单个锁是串行的。

  • 分段锁最多支持16把锁,所以并发不如cas。


加锁的时候加入了写锁,但是读不加锁。

可并行的线程和哈希表的分段大小相关。


hashTable所有操作都是加锁的,所以并发不行。

hashMap有线程不安全的问题。


哈希表扩容

双倍扩容。当数据的数量大于数据量 * 负载因子的时候会触发扩容的操作。

当单链的长度大于8的时候会转化为红黑树,小于6则退化为链表。原因是因为会发生哈希碰撞导致时间复杂度退化到N ^ 2


哈希表的默认大小必须是16的整数倍(最小是16)且大小是2 * N次方,以便取模进行位运算。


迭代器方法

Java的for each实现的时候会使用迭代器。而迭代器会比较modCount记录的修改次数,如果ModCount增加的时候会抛出异常。

而普通的for方法不比较modCount,所以会导致修改。


但是:当修改hash表中的数据的时候,modCount不会变化。只有在移除和增加节点的时候modCount才会发生变化。


哈希值的计算方式

是通过高16位和低16位进行异或操作,让数据尽可能的分散。(遍历链表的时间复杂度为N,而遍历数组的时间复杂度是1)


哈希表的时间复杂度

哈希表的时间复杂度均摊为1。

哈希表的设计保证了:基本上不会发生哈希碰撞,链表的时间复杂度是N,哈希表的时间复杂度是LogN这些懂事小概率事件。

所以可以表示为数据的时间复杂度为1


TreeMap的底层是通过红黑树来实现,他的平均时间复杂度是OLogN。

为什么使用红黑树:因为会进行左旋右旋以保证二叉树的深度是LogN,而不会出现深度过高,保证查询的时间复杂度是LogN。

但是左旋和右旋的操作也比较消耗性能。所以相对传统的AVL树,红黑树保证每次插入不超过3次修改(我到现在还没有实现过红黑树……)

哈希表有序

哈希表因为会出现哈希,所以读取的时候会乱序。

如果要有序的话需要使用LinkedHashMap来实现。


重写hashCode方法

哈希表比较的时候如果是自定义的类需要重写hashCode方法和equals方法

原因:

  • 不重写的话是直接比较内存地址
  • 而比较之前需要比较哈希值然后比较数据

null值

HashMap支持null,但是hashTable不支持。

原因:null没有hashCode方法,会抛出空指针异常。而hashMap加入了判断

key = null return 0


CurrentHashMap不支持Null值。因为:

  • 可能会出现歧义,是不存在还是null,为了防止这个问题所以直接不用
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值