首页 > 编程知识 正文

arraylist扩容机制面试题,redis底层实现原理面试题

时间:2023-05-03 19:24:28 阅读:30268 作者:517

扩展机制1.什么时候才需要扩容第一次调用put方法时初始化数组table

如果HashMap中的元素数超过数组大小(数组长度) loadFactor,则会扩展数组,loadFactor的默认值(DEFAULT_LOAD_FACTOR )为0.75。 这是折中的值。 即,如果阵列大小默认为16,则当混列映射中的元素的数目超过160.75=12 (该值为阈值或边界值threshold值)时,阵列的大小将扩展为216=32,即,两倍

如果HashMap链表中的对象数达到8,如果数组长度未达到64,则HashMap先扩展解决;如果达到64,则该链表变为红黑树,节点类型为Node到treenode tator 当然,在删除映射关系后,如果下次运行resize方法时确定树的节点数小于6,树也会转换为链表。

要扩展2.HashMap的扩容是什么,必须重新进行一次混列分配。 此外,必须遍历hash表中的所有元素,这需要很长时间。 在写程序中,要尽量避免resize。

混洗映射在进行扩展时,使用了非常巧妙的混洗方式。 由于每次扩展都是两倍,因此与原始计算的(n-1 )混列结果相比,只需要增加一个位。 因此,该节点位于原始位置或被分配给名为'原位置+旧容量'的位置。

怎么理解? 例如,从16扩展到32时,具体变化如下。

因此,元素在重新计算散列后,n会加倍,所以新的索引会发生这样的变化,因为n-1的标记范围多了前1位(红色)。

说明: 5是假设计算出的原始索引。 据此,上述说明得到了验证。 扩展后,节点位于其原始位置或被指定为'原位置+旧容量'。

因此,在扩展散列映射时,我们不需要重新计算散列,只需要看到原始散列值中新添加的位是1还是0即可。 如果为0,则索引不变。 1则索引为“原始索引oldcap(http://www.Sina.com/)”。 下图显示了从16扩展到32的resize的图像。

由于这样巧妙的混列方式,不仅省去了重新计算混列值的麻烦,而且认为新追加的1bit是0还是1是随机的,因此在resize的过程中,混列之后的各桶的节点数一定在原桶的节点数以下

3 .源代码resize方法的解密代码的具体实现如下:

final NodeK,V[] resize () /当前数组NodeK,V[] oldTab=table; //如果当前数组等于null长度,则返回0;否则,返回当前数组的长度intoldcap=(oldtab==null )吗? 0 : oldTab.length; //当前阈值点默认为12(16*0.75 ) int oldThr=threshold; int newCap,newThr=0; //如果旧数组的长度大于0 //,扩展后的大小if(oldcap0)//超过最大值就无法扩展,只能和你一起碰撞。 if(oldcap=maximum_capacity )//修正阈值为int的最大值ThreShold=int return oldTab; (/*未超过最大值的、扩展为原来两倍的1 () newCap=oldCap 1)将MAXIMUM_CAPACITY扩展为两倍后的容量小于最大容量2 ) old cap=default _ initial ) ) ) ) ) 65 ELSEif(newcap=oldcap1) maximum _ capacityoldcap=default _ initial _ capacity ) /将阈值扩大一倍//double threshold } //旧阈值点大于0的直接赋值else i

f (oldThr > 0) // 老阈值赋值给新的数组长度 newCap = oldThr; else {// 直接使用默认值 newCap = DEFAULT_INITIAL_CAPACITY;//16 newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); } // 计算新的resize最大上限 if (newThr == 0) { float ft = (float)newCap * loadFactor; newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? (int)ft : Integer.MAX_VALUE); } //新的阀值 默认原来是12 乘以2之后变为24 threshold = newThr; //创建新的哈希表 @SuppressWarnings({"rawtypes","unchecked"}) //newCap是新的数组长度--》32 Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap]; table = newTab; //判断旧数组是否等于空 if (oldTab != null) { // 把每个bucket都移动到新的buckets中 //遍历旧的哈希表的每个桶,重新计算桶里元素的新位置 for (int j = 0; j < oldCap; ++j) { Node<K,V> e; if ((e = oldTab[j]) != null) { //原来的数据赋值为null 便于GC回收 oldTab[j] = null; //判断数组是否有下一个引用 if (e.next == null) //没有下一个引用,说明不是链表,当前桶上只有一个键值对,直接插入 newTab[e.hash & (newCap - 1)] = e; //判断是否是红黑树 else if (e instanceof TreeNode) //说明是红黑树来处理冲突的,则调用相关方法把树分开 ((TreeNode<K,V>)e).split(this, newTab, j, oldCap); else { // 采用链表处理冲突 Node<K,V> loHead = null, loTail = null; Node<K,V> hiHead = null, hiTail = null; Node<K,V> next; //通过上述讲解的原理来计算节点的新位置 do { // 原索引 next = e.next; //这里来判断如果等于true e这个节点在resize之后不需要移动位置 if ((e.hash & oldCap) == 0) { if (loTail == null) loHead = e; else loTail.next = e; loTail = e; } // 原索引+oldCap else { if (hiTail == null) hiHead = e; else hiTail.next = e; hiTail = e; } } while ((e = next) != null); // 原索引放到bucket里 if (loTail != null) { loTail.next = null; newTab[j] = loHead; } // 原索引+oldCap放到bucket里 if (hiTail != null) { hiTail.next = null; newTab[j + oldCap] = hiHead; } } } } } return newTab;}

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。