java集合复习与巩固(一)

HashMap这是一个老生常谈的问题,但是不经常看的话很容易忽略。特此记录,以增强记忆。

首先我们来说下数组,由于内存空间是连续的所以位置可以通过基本地址和偏移量计算出来,这也是为什么数组查询快的原因。至于插入删除嘛,所需要移动元素可能过多。所以写比较慢

在来说下链表,链表的节点在内存中位置不是联系的需要通过上个元素的指针去寻找下一个元素所以寻址方法时间复杂度是O(n)

但是插入的话只是断开相邻节点的连接位置,连接到新的节点收尾即可。所以插入较数组来说效率要高一些。

那么问题来了,有没有一种这两个特点都具备的数据结构来解决我们的问题呢?这时候就有了HashMap。

首先我们来了解下HashMap的结构

hashMap重要参数

//默认的初始容量为16,必须是2的幂次
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; 

//最大容量即2的30次方
static final int MAXIMUM_CAPACITY = 1 << 30;

//默认加载因子
static final float DEFAULT_LOAD_FACTOR = 0.75f;

//当put一个元素时,其链表长度达到8时将链表转换为红黑树
static final int TREEIFY_THRESHOLD = 8;

//链表长度小于6时,解散红黑树
static final int UNTREEIFY_THRESHOLD = 6;

//默认的最小的扩容量64,为避免重新扩容冲突,至少为4 * TREEIFY_THRESHOLD=32,即默认初始容量的2倍
static final int MIN_TREEIFY_CAPACITY = 64;

HashMap的put()和get()的实现

1、map.put(k,v)实现原理

第一步首先将k,v封装到Node对象当中(节点)。第二步它的底层会调用K的hashCode()方法得出hash值。第三步通过哈希表函数/哈希算法,将hash值转换成数组的下标,下标位置上如果没有任何元素,就把Node添加到这个位置上。如果说下标对应的位置上有链表。此时,就会拿着k和链表上每个节点的k进行equal。如果所有的equals方法返回都是false,那么这个新的节点将被添加到链表的末尾。如其中有一个equals返回了true,那么这个节点的value将会被覆盖。

2、map.get(k)实现原理

第一步:先调用k的hashCode()方法得出哈希值,并通过哈希算法转换成数组的下标。第二步:通过上一步哈希算法转换成数组的下标之后,在通过数组下标快速定位到某个位置上。重点理解如果这个位置上什么都没有,则返回null。如果这个位置上有单向链表,那么它就会拿着参数K和单向链表上的每一个节点的K进行equals,如果所有equals方法都返回false,则get方法返回null。如果其中一个节点的K和参数K进行equals返回true,那么此时该节点的value就是我们要找的value了,get方法最终返回这个要找的value。

扩容为什么是2的幂次

 int index =key.hashCode()&(length-1); 初始值是16的话 那么 就是  hashcode&1111.这样得到的结果可以保证hashcode后4位的值不变 从而减少hash碰撞。使得节点均匀分布 减少产生长链表或者红黑树的情况。(这样理解不知道对不对)

红黑树 8 6

当链表上的节点超过8个时链表转换为红黑树,小于6个时红黑树转换成链表。

红黑树的查询时间复杂度是log(n) 链表的查询复杂度是 n/2 。当长度为8时 log(8) = 3 ;  8/4 = 4。此时红黑树的查询时间小于链表

但是TreeNodes占用空间是普通Nodes的两倍,所以只有当bin包含足够多的节点时才会转成TreeNodes。这样就解析了为什么不是一开始就将其转换为TreeNodes,而是需要一定节点数才转为TreeNodes,说白了就是空间和时间的权衡。

当hashCode离散性很好的时候,用到红黑树的概率非常小。通俗点将就是put进去的key进行计算hashCode时 只要选择计算hash值的算法足够好(hash碰撞率极低),从而遵循泊松分布,使得桶中挂载的bin的数量等于8的概率非常小,从而转换为红黑树的概率也小,反之则概率大。

面试题

HashMap的工作原理是什么?

HashMap中的“死锁”是怎么回事?

高并发场景中容易出现死锁

//高能预警!!!!重点全在这个函数中  void transfer(Entry[] newTable, boolean rehash) 

假如转移前链表顺序是1->2->3,那么转移后就会变成3->2->1。这时候就有点头绪了,死锁问题不就是因为1->2的同时2->1造成的吗?所以,HashMap 的死锁问题就出在这个transfer()函数上。单线程不会出现这样的问题。

 

HashMap中能put两个相同key吗?为什么?

如果不重写key的hashCode()方法相同key的不同值会覆盖。同理只要重写了key的hashCode()和map的put()方法,应该就可以实现对于相同key下多个value的存储。

具体做法是:由于判断key是否存在的时候是先比较key的hashCode,再比较相等或equals的,所以重写hashCode()和equals()方法即可实现添加重复元素。重写这两个方法之后就可以覆盖重复的键值对,如果需要对value进行叠加,调用put()方法之前用containsKey()方法判断是否有重复的键值,如果有,则用get()方法获取原有的value,再加上新加入的value即可。
 

HashMap中的键值可以为null吗?原理?

可以 为null的话 hash值返回0

HashMap扩容机制?

切记初始化容量,可以根据阈值来算 初始化容量*0.75=需要容量。Hash 表的尺寸和容量非常的重要。一般来说,Hash 表这个容器当有数据要插入时,都会检查容量有没有超过设定的 thredhold,如果超过,需要增大 Hash 表的尺寸,但是这样一来,整个 Hash 表里的无素都需要被重算一遍。这叫 rehash,这个成本相当的大。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值