粉丝9224获赞6.1万

有假发,我自信。这一期我们来说说 jdk 一点八中的 hasmap 的扩容机制。首先我们来看看 hasmap 的初始容量, hashmap 如果使用默认的方式创建,默认的初始容量是十六,如果我们指定了初始容量,比如指定为二十四,实际上 hasmap 会通过计算得到一个 大于等于二十四的二的整次秘书,三十二作为初始容量。为什么呢?因为二的正次秘书有统一的规律,可以通过为运算快速的计算出元素的,所以关于所以的计算在上一个视频中已经讲解过,这里就不再坠述。接下来我们来看看 hex map 扩容的情况。 hex map 在两种不同的情况下会扩容。第 第一就是元素个数到达了扩容的预值,这个扩容的预值是通过容量和负载因子计算的,默认的负载因子是零点七五,也就是说如果数组长度是十六,那么扩容的预值就是十二。如果数 数组的长度是三十二,那么扩容预值就是二十四,当然这个预值也是有最大值的。这部分后面再说。了解到这里,也就是说第一种扩容的情况就是当元素个数超过了当前的扩容预值,就会将数组扩容。那么接下来说第二种扩容的情况,在还是 map 中有一个最小数化容量 确认值是六十四。假如数组的某个位置上的链表个数到达了八个,但是数组的长度还没有到达六十四。 hash map 不会竖画这个链表,而是对数组进行扩容。这也是上上个视频中有朋友说我讲的有问题的部分。继续说 hash map 如何扩容?如果需要扩容, smap 会对原有的容量和扩容预值进行左移一位的运算,其实就是翻倍。根据新计算出来的容量值和预值创建新的数组,然后将原来数组中的所有元素重新计算位置,并且移动到新的数组上 对应的位置。当数组的长度大于或者等于六十四之后,如果某个列表的元素到达八个 hash map, 就会将这个列表转换为红黑数,当少于六个的时候又会转换为列表。继续说说 hash map 的容量上限。 house map 定义了一个最大的容量值 2 的 34 方,当数组的容量超过或者等于 2 的 34 方时,扩容的预值会直接设置为整形的最大值,也就是 2 的 31 次方减一。当然到达最大值的时候还是 map 也不会再扩容了。可能有人要问,不再扩容, 如果元素过多怎么办?其实一般情况都是还没有到最大值,内存就一出了。如果真的到达了最大值,并且数组所有位置都不为空,那么就是继续给对应位置上的列表或者红黑数继续添加新的节点,这就是 jdk 一点八中还是 map 的扩容。

他就可以通过这个逻辑得到一个数据下标,得到数据下标之后往前面讲了,接下来呢就去判断当前这个位置上面你的 k 是不是相同的,哎啊,当然他这里呢也用到了一个思想,就是先比较好去扣的,就是我现在想要去判断 我的某两个 k, 就说我我便利到了某个安全对象,我要判断你的 k 和我传进来 k 是不是相同的,那么他先判断的是还缺扣的,因为我们通常呃有有有有有那么一个思想嘛,对不对?就是我两个 对象的哈系扣的如果相等,那么你的你的 k, 呃才有才,就一一呃哈系扣的相等,那么你的 k 有可能相等,如果哈系扣的不相等,那么你的 k 呢?就呃呃,不是啊,就是 好,那么算出来下标之后啊,我们紧接着还会去判断你当前这个位置上面 呃是不是存在相同的 k, 对不对啊?如果说存在的话呢,那么他就我敢说了吗?去进行外旅的一些覆盖,然后返回后的外旅,然后呢再调用这个方法啊,最终就是去判断要不要扩容,然后最终就就去存一下,所以说接下来呢,我们就来好好的看一下在哈西麦府里面他这个扩容 是怎样的,好吧,就是当我现在要去铺的一个 ky 扭进来了,那么呃我我铺的的时候我要去判断扩容,你看他怎么判断的,到时候先判断我当前这个哈西麦粉里面我总共存的多少个元素了,我已经存了多少个元素了,就是这个塞子,然后这个 suri 的后的怎么来的呢?他其实就是在我们刚刚 这个位置啊,就是他,他他会会去算一下,怎么算的呢?他就是拿你算出来的这个数字的容量乘以加在音质默认是零点七五啊,就乘一下默认情况下面就是十六,乘以零点 七五,也就是十二,就是默认情况下面这个扩容的这个预值呢?就是十二,就是在这个里面啊,他如果默认情况下面,他判断出来,我当本次铺的我还没有去存我的 k 二八六,我这个时候我已如果说已经存了呃至少十二个元素了,那么我可能就要扩容 啊,但是他只是可能,因为如果说要扩容,我这里还有一个条件,这也是很多同学很多呃,一些博客上面他都没有去说的一个点啊。就是只有说你这个条件也符合,我才会去扩容,那是什么条件呢?就是说当你铺的一个 k y 进来的时候啊,比如说这个 k y 进来的时候,他要放到呃某个位置, 他但是他如果判断出来他说要放的这个位置如果不是空的,明白吗?如果不是空的情况下面,并且你整个哈西麦普也已经存的至少十二个元素了,他才去扩容。假设我整个哈西麦普 我现在已经存了十二个元素了,但是你当前铺的一个新的 k 发率进来,你所要放的这个位置他是空的,那么他就也不会进行扩容。选择是这个代码的一个意思啊,如果要放在那个位置他是空的,就算你现在哈士卖部里面已经有一些元素了,他也不会去进行扩容。 好,所以说符合这两个条件之后他才会去掉这个瑞塞子方法去进行扩容,那么这又是 怎么扩容的呢?对二乘以什么?什么 tvs r type s, 就是表示当前这个扩容之前的速度的大小嘛,比如默认十六,对,我现在扩容了,那我就乘以十六,那就是三十二,就是双倍 扩容啊,那么到底怎么扩容的?首先啊,这里呢,也去取一下老数字的一个大小呀,然后去比较一下,我们直接看这里,好吧,首先溜出来一个新的速度,也就是我现在针对,比如说针对这个速, 我要去扩容,那么他怎么扩容的呢?哎,我先把这里大概缩小一点,好吧,让大家好讲一点,我首先去溜出来一个新的双倍,我之前那个速度双倍大小的一个速度就这样子,对啊,溜出来一个新的速度啊,溜出来之后我们再来看,然后我紧接着就去圈死粉 啊圈师傅,说白了就是转移元素,怎么转移的?我们大家看啊,反正转移完了之后呢,我就会把新的数组复制给哈新麦谱里面这个特博属性,然后重新算一下预值,哎,这个就这个就比较好理解吧。对,好,那么我们再来看这个怎么转移的啊?当然在真正 转移元素之前他会掉这个方法,那这个方法呢,其实跟瑞哈西就是有关系的,这个方法干嘛的?我等下来说,我暂时就先告诉大家这个方法,大部分情况下面他返回的都是 fuse, 好吧,啊,那么为什么我等下会解释啊? 好,我们先看他怎么转移的啊,大家可以想一下这个我,我现在这是老数组,这是新数组,那么我老数组上面可能本来就就已经有一些元素了。对我就随便写一下。这里可能啊已经存的一些。呃安全对象了吗?我随便写一下,那么呃包括这边也是一样的,我也稍微的就就就随便换一下好吧。 啊好,这是我老数上面所存的元素,那我现在要转移再想一下怎么转。哎。是不是先从这个位置上面 啊把这个位置上面比如说这那如果说有列表的话我把这个位置上面的列表的每一个元素先转移过去然后再转这个再转这个再转转转转转转。 是是不是这样一个逻辑?说实话他赚代码体现出来的也是你看他是一个双重循环循环也就是说我一开始便利数组便利数组上面的每一个元素然后再去看你当前呃数组上面这个元素他是不是 对应的一个列表他的 next 属性是不是有值啊如果有值呢我就一步一步的再去便利当前这个位置上面列表上面的元素属实是一个双重的循环。对所以说你大概可以看懂啊便利。哎。啊如果说不等于空不等于空的话 我就先取先记录一下 nice 的看到没有。先记录一下 nice 的,然后呢啊瑞哈西他默认下面是 fuse, 你先不管然后干嘛呢直接用一点哈七值, 这这个一呢比就也就是我们之前比如说我现在转移他了就把这个安全对象的哈气值给拿出来。拿出来干嘛呢?用你心宿主的容量调音 dancle 去算出来一个新的下标, 你看嘛所以说就还是值,因为他没有变化嘛,变的是我们数字的容量,所以呢,我们再去算出来一个新的数字下标,然后我在干嘛呢?我就一 点 nex 的,就指向新的数字下标,怎么做的?他其实这样子,假如说我现在要转移这个元素,这样那么一点 nex 表示谁呢?表示的就是我们这个 箭头嘛,就是个数,相当于这个指针嘛?对,指向我新的数字下面,比如说我也指向他,好吧,指完了之后你看在干嘛?一等于六,特别二,六特别二等于一,那这是干嘛?其实就是我前面说的向下移, 明白吗?他其实就是把你这个元素复制给了当前这个数字上面的这个位置,也就说我是这样子的, 厉害吗?但我这里是个空啊,所以说你你写和不写就是无所谓,我是表达一下这个效果,它是这样子实现的。最后因为我一开始就记录了一下 nice 的嘛,我这个 nice 一开始指向的其实是他,对不对?所以这个时候呢,他就把一啊又又指向了我的, 呃,这个元素,那我稍微的画一下吧,我这里去掉啊,我稍微稍微的画一下。那这是,呃, next 的那个属性,他其实这个时候只像他,但是呢,呃,当我执行完自然代码之后呢,我就会把 next 呢就指向一,所以说 next 对着这个元素啊,我就一开始指向他 就是这个呢,我们的一啊,他其实也会指向这个元素,对,这是一啊,然后紧接着我就第二次循环,对,紧接着第二次循环的话,呃,一不等于空,不等于空。一等一点 next 复制给 nex, 所以这个时候其实相当于这个 nex 属性,他会指向下一个元素了。对,然后呢我再去转移这个元素。这个元素怎么转移的呢?其实按照我们的代码也是这样的, nex 的。呃,先不管他,然后呢一点 nex 等于六退把 i 啊,也是这样的,先根据你当前这个一去 算一个数,新的数字下标,然后呢一点 nice 二等于 nute 吧,也就是这个元素所 next 的属性。我只先 nice 吧,假设也是这个位置。好吧,也是这个位置,那我就指过去。指过去之后我刚讲了嘛,他还有一一个步骤,就是干嘛 就把一复制给新数组刚刚对应的那个位置,那也就是我刚刚说的往下移啊。来我们这里稍微的呃,就给大家演示一下,其实这个图随便你这么画都可以。对,紧接着其实就把你 刚刚这个一所对应这个元素呢复制到新数组的这个位置上面来往下移,明白吗?然后呢干嘛再把一呃等于 nex 就把修改一下这个一的值就复制给他, 对不对?然后呢再把 nice 再吗啊就进行下一次循环啊,一还不等于空,也还不等于空,那 nice 走运再指向指定是空啊, 就没了这个元素的下一个是空吗?看到没有是空,所以说,呃,再继续的话就再算一个新的下标出来 啊。对对对,就算他了吗就转移他了,所以说他最后呢他其实我就不去详细话了。好吧,他就最后也是这样,只让他,然后再整体的相当于。啊啊,我看前面说了这个过程呢,所以大家理解一下,所以这种就是这样子,这样就完成了。呃,我们当前 这个数组上面这个位置上面所有的元素的转移发现没有?但是我们可以发现一个问题啊,就是我们这么转移的话,你就会发现 本来我这个位置上面存的这个顺序是一二三,但是我转了之后我发现他变成了三二一了, 厉害吗?好,这是一个问题。同样的其实还有呃一些呃技巧在里面,还有一些问题在里面,比如说什么呢?啊?我们刚刚这个位置上面的元素啊,你现在转所转移之后 后心术上面的这个下标他到底有没有什么规律呢?其实是有一些规律的啊,大家可以看一下啊,有什么规律呢?我自己先补好啊。 啊?有什么规律呢?比如说这是我老数字上面的三个元素,对,那么我一开始我这三个元素对应的位置都是零,就是二嘛,这三个元素对应的位置都是二,对不对?下边都是二啊?那么我转移到新数字上面去之后, 我的这个下标他是随机的,还是会有什么规律呢?其实这个地方是有规律的,我们来看一下,就是我同样的一个还是扣的,别说啦,他啊,这是我老宿主 啊所算出来的呃位置,就是刚刚说的这个进这个结果,对,那我同样的我拿这个同样的这个哈气扣的啊,然后我基于新宿主,因为新宿主他扩容之后的大学就是三十二,三十二减一呢,就是三十一,对不对?那么三十一你所对呢 二金值是多少呢?就这样子来,自己可以去算一下,这就是三十一。好吧,那么三十一我同样的拿这个 happy 后的我去与我算一个新的下边出来,那么这个新的下边的结果是多少呢?我们发现这后四位其实没有变,变的就是他, 对不对啊?这里呢就变成了一,所以这是老的数字下标,这是新的数字下标。那么呢,我们来比较一下这新老两个数字下标它的一个区别在哪里? 是不是个新的数字下标,他在老的数字下标之上加了这个一,那加的是多少呢?加的就是十六。最后我们发现新的下标的值是等于老的加十六 面吗?我随便写一下就中音混合,好吧,就是新的值,新的数字下边的一个规律。好,当然还有一个点,那假设我这个还是扣的,我这一位上面不是一,我是零呢?这个还是扣 后的,你这个随便改吗?如果说你是零呢?哎,那么这里呢?其实他也发现如果这是零,那么这里呢就肯定还是零,那么下面的新数组下标呢?其实也是零,明白吗?所以发现这个新数组的下标呢,也有可能就是老数的下标,但是我们发现不管你怎么改 免吗?但是你不管,你再去呃呃,假设任何其他的哈序扣的,其实也 就在这两种两个结果里面里面,你这一位上面你就发生变化,其实不影响的,包括你这些位上面你去发生变化也不影响的,你唯一就是这一位上面你发生就不同的情况的话,你最终啊,你这个心脑数足的下标就有一点不一样,那么 不管怎么样,也就这两种情况,就说我们新扩容之后的某个安全节点,他在新数组上面的下标啊 和老数字下边的关系要么相等,要么就是加十六,也就是加扩容之前这个数字的大小,所以说像我们这种情况我这三个元素要么呢就在这里 对不对啊?要么呢比如说第二个元素要么可能在这里,要么可能呢就是加上的呃,就是对应的某个位置吧,你自己去加一下,我这里看不太清,好吧,反正大家就知道,反正就是在我这个数字我目前大概是八吗?所以二加八应该是十一啊,零一二三四五六七八九应该是这里吧, 对不对啊?零一二应该是十啊,对应该是这里,对吧?啊?不是就这样子啊,所以说,呃,包括像这个也是一样的,明白吗?包括这个就是有可能这里,有可能这里啊,你就看具体的他这个还是扣的是多少啊?啊?零一二三四五六七八九十啊,是这里啊, 所以说呃就是这样的一个呃情况,好吧,所以说这也是我们哈西麦普他在扩容的呃前后他这个下边的一个规律,所以说在一点八里面他其实呃就没有这么难实现了,他直接就是我按照感恩的一个规律啊,他去判断他到底是呃加十六呢还是相等 啊?所以说这是呃这样子的啊,所以说是扩容啊,当然嗯跟扩容相关的其实还有一个比较复杂的点,就是在多线程扩容的情况下面他有可能会出现线程不安全的问题,好,我们等下再来讲啊。好。

原码分析, hashmap 什么时候进行扩容?为什么扩容因子设定为零点七五? hello, 我是架构师奶爸,为了减少哈西冲突发生的概率,当当前 hashmap 的元素个数达到一个临界值的时候,就会触发扩容。 把所有元素 rehash 之后,再放在扩容后的容器中,这是一个相当耗时的操作。原码如上所示,而这个临界值 threshold 就是由加载因子和当前容器的容量大小来确定的。 假如采用默认的构造方法,临界值 threshold defaultinity or capacity default load factor。 原码如上所示,那就是大于十六乘以零点七五等于十二时,就会触发扩容操作。那么为什么选择了零点 七五作为 hash map 的默认加载因子呢?简单来说,这是对空间成本和时间成本平衡的考虑。在 hash map 中有这样一段注视元马,如上所示。我们都知道 hash map 的散列构造方式是 hash 取于 负载因子,决定元素个数达到多少时候扩容。假如我们设的比较大,元素比较多,空位比较少的时候才扩容,那么发生哈西冲突的概率就增加了,查找的时间成本就增加了。我们设的比较小的话,元素比较少, 空位比较多的时候就扩容了,发生哈吸碰撞的概率就降低了。查找时间成本降低,但是就需要更多的空间去存储元素,空间成本就增加了。因此, 零点七五这个数值是在平衡内存使用和性能后选择的一个相对合适的结果。想了解更多 java 架构师岗位知识,请关注我架构师奶爸,点赞转发收藏,共同筑基 java 架构师!

大家好,我是抖音白竹卫的, 今天讲解哈士马的破方法,我们先看下目录,我会先从简单介绍破方法的实现过程,然后再对原码逐句讲解。一、首先根据插入的 k 计算他的哈士值, 通过哈士马的哈士扣的方法计算得到。二、然后通过预值判断是否需要进行扩容,如果当前元素数量超过了容量和负载因子的成绩, 就会进行扩容操作。三、创建或获取数组 table, 该数组存储了 harshmap 的节点,每个节点是一个 entry 对象,包含了建制队。四、如果对应的 harsh 曹为空,则直接插入 经验处理对象到该曹位。五、如果曹位不为空,则需要执行以下步骤, a、 判断插入的 t 与曹位中已存在的键的哈是值和 t 是否相等,如果相等,说明是同一个键, 直接替换对应的值即可。 b、 如果和时值冲突及不同键的和时值相同,需要便利链表或红黑数。 gdk 八中链表长度大于等于八十,会转换为红黑数来查找是否存在相同的键。 c、 如果存在相同的键,直接替换对应的值。 第一,如果不存在相同的键,将新 intro 一对象插入到链表或红黑数的末尾。需要注意的是,在插入新的 intro 对象后,如果节点数量超过了 shifi, 自首默认为八,则会将链表 转换为红黑术来提高查询效率。总结起来, jdk 一点八的哈士骂的破方法实现过程主要包括计算哈士时,判断是否需要扩容、定位、槽位、处理冲突等步骤。下面是破源妈解读,根据哈士时计算统的所引位置, 如果该所引位置为空,则直接创建一个新的节点存放建职队。如果添加节点后的大小超过了预值,则进行扩容 插入节点后的操作。如果该锁引位置上的第一个节点的替满足条件相等或者哈是冲突,则将其复制给。 如果该锁引位置上的第一个节点是红黑数的节点,则采用红黑数的节点插入方式来插入新节点,否则便利该锁引位置上的链表。查找相同替的节点。 如果链表中不存在相同 t 的节点,则将新节点插入到链表的末尾。如果链表的长度达到预值,吃叶 fi 子瑞手,则将链表转换为红黑数。如果在链表中找到相同 t 的节点,则结束循环。 如果找到相同 t 的节点,则替换对应的 w 记录节点访问顺序。

好,接下来我们来看一个哈西麦普非常高频的面试题,来讲一下哈西麦普的 poot 过程。好,首先呢,我们是按照整个流程图来给面试官讲啊,我们把这个流程讲清楚其实就 ok 了,来, 我们看一下这个流程啊,首先我们在铺的时候呢,我们会先判断这一个数组,就整个数组是不是空的, 如果这个数组都是空的,哎,首先我们会对这个数组进行扩容。好,然后接下来呢,我们会先取到这个 k 队的哈西直,然后再根据哈西直和咱们数组长度 做一个语音,算得到咱们的一个哈西槽。好,关于哈西槽上面呢,就分四种情况啊,第一种,比如说这个哈西槽上面没有东西是空的,还有一种呢,当前哈西槽上面就只有一个元素,还有一种 情况是,哎,他是一个链表。第四种情况呢,他可能是一个红黑树。好,接下来呢,我们先看他的逻辑啊,首先判断这个,呃,哈西槽上面的元素是空的,如果比如说他是空的啊,直接插入,那如果当前呢有元素,并且这个 k 就判断他的 k 是不是一致的,如果当前哈西藏上面的元素的 k 和我们插入这个元素的 k 是一致的,那么直接覆盖啊,如果不一致的话, 我们首先会判断当前这个节点是不是一棵树。好,如果他是一棵红会树的话,按照树来操作。好,如果他不是一棵树,那就只剩一种情况,他是一个列表,如果是列表的话,我们会开始便利这个列, 然后依次的去判断链表中是否存在这个 k, 如果找到了,直接覆盖, 如果没有找到, a 会在我们链表后面呢?追加一个元素,然后追加完之后呢,我们再判断当前链表的长度是否大于八, 如果不大于八直接返回,如果大于八,他会将我们的链表转成红黑数。整个流程结束过后呢, 再来判。好这个地方其实就是做了一个判断啊,当前我们哈西槽的使用率是不是超过我们整个数组的四分之三?是否超过预值?如果是的话,哎,会结束之前对我们的哈西麦普进行扩容,好结束。

好,那我们再来看另外一道看看的话是迈普的扩容机制,应该就是问的底层原理,对吧?好,那我们来看一下啊,其实呃,也是一样的,首先 啊,你也是要去进行一个区分,就是针对一点七的版本和一点八的版本进行区分啊,因为啊这个感触还挺大啊。就是,呃,在一点七里面,我们的康康拿去迈普,他是基于 c 格门的来进行分段实现的。什么意思?我给大家看一个图 啊,其实在一点七的中间,我们的康康纳新迈普里面,他其实最外层是一个 c 个门的类型的一个竖柱,里面存的是一个一个的 c 个门的对象,然后每一个 c 个门的对象他 他的内部又有一个数组,然后就像哈西迈普一样,每个位置他也可以去存一些列表。好,所以说这是他呃,一点七 七的一个构造,所以说一点七里面我们的看看那些迈步,当你去铺的一个元素的时候,其实你首先就会去进行 c 个门对象去进行加锁,对吧?当然同样的啊,如果说我们单纯的只是考虑扩容的话,那么在一点七里面是每一个 c 个门的对向内部去进行一个扩容,比如说 四个,当前这个位置是一个美略线内部的数组,目前是四个,但是如果说现在又有元素破的进来,一直破的,一直破的,对吧?那么最终就会进行扩容,就会把这四个哎去进行一个双倍哎,就变成八个的一个数组, 对吧?但是并不影响其他的四个门的对象,是吧?所以说啊,呃,这是一点七里面,他这个做法就是说当我们需要进行扩容,那那么也是这样的,先生的行车速度,然后转移元素在行车速度中,然后但是是每个四个门的内部单独去进行 扩容的好,这是一点七里面的好,那么一点八里面就不一样了,因为一点八他不再基于西格门的来进行实现,所以当某个县城进行铺特时,哎,看看那些迈普,他如果说他发现正在他自己正在进行扩容的话,那么该县城就会一起去进行扩容好, 如果某个县城铺等的时候发现没有在进行扩容,那么则会将 kow 添加到 ctrl ctrl 中啊,然后去判断,哎, 是不是超过了预值,然后再会去呃,看要不要进行扩容啊,就说是先添加进去,再去判断要不要进行扩容好。 同时第四点就是说看看拉面,看看那些迈步,他是支持多个县城同时扩容的,那么怎么扩容的呢?其实也是这样的,扩容之前也会先去生成一个宿主,但是在转 以元素的时候,哎,他是会呃按不同的线程去进行一个分组的,比如说我刚刚讲的啊,当然,呃,就像我们之前啊给大家讲的假设啊,这是一个很简单的一个元素组,大家给大家画一下啊 啊,一个元素组,然后我元素上面呢可能也会呃,比如说有八个,对吧?这是八个元素组, 然后我把这里缩小一点点,哎,缩小一些,然后我们扩容的时候,哎,看看的是迈普也是这样的啊,他先会生成一个双倍大小的一个新的数组, 好身心的速度。之后,哎,比如说你元素上面这边不是有元素吗?哎,那么我现在有多个现成,比如说我第一个现成,我负责这两个位置上面的元素的转移,哎,我后面另外一个现成,我负责这两个,对不对啊?假设我就两个现成,那么我前面这个现成,我 把这两个位置上面的元素转移完了之后呢,我就会来转移这两个位置上面的,就这种,明白吗?所以他有一个不长的一个概念啊,所以这是看看那些面粉里面他多现成扩容的一个操作。好吧,所以当然怎么扩容的?和我们, 呃,就前面给大家讲的一点七里面的哈西迈布的扩容的步骤,或者说一点八里面的,呃,哈西迈步的扩容步骤其实是一样的。类似的啊,你可以再去具体分一下具体的版本,好吧?嗯。

开始迈步的底层数据结构是什么?开始迈步的数据结构是哈西表,哈西表是由数组和列表构成的。初始容量是多少?负载因子又是多少?初始容量是十六,负载因子是零点七五。 容量不够的时候怎么办呢?当插入元素个数达到总容量大小的零点七五的时候,就会进行扩容,每次扩容的容量都是原来容量的两倍。你 nice。

哈西麦普的使用经验分享,如果我们可以预估出哈西麦普最终存放值的个数,那么在初始化的时候最好能促进哈西麦普的容量大小,从而避免哈西麦普进行多次扩容。因为哈西麦普的每一次扩容操作都是一项非常耗时的任务, 我们可以根据哈西麦普的原理来了解一下扩容耗时的原因。哈西麦普在不设置容量大小时,默认容量大小是十六,在第一次使用时会出处话一个长度为十六的数字。 我们在存放数据时,哈西麦不会计算存放数据 k 的哈吸值,然后与当前容量大小去于操作,从而得到该数据存放在数组里面的摄影值,使我们的数据尽可能离散的分散到数组的各个位置。 我们知道数组的地址空间是连续的,查询可以根据,所以快速定位数据。数组的查询时间复杂度是欧一, 但是当我们哈西运算得到的熟练值出现重复的时候,俗称哈西碰撞,那么相同熟练值的数据将以电表的形式存储。电表的积水空间是非连续的,查询只能便利每一个接点,查询效率非常低,查询的时间复杂都是欧恩。 所以当电表长度大于八时,他是卖不会把电表转化成一颗红黑树,从而提高查询效率。红黑树的查询时间复杂度是 ologun。 那么如何保证我们哈西麦普整体的查询效率最高?很显然,答案是让数据尽可能都落到我们数组里面。为了保证数据尽可能多地落到我们数组里面,哈西麦普引入了一个装载因子的概念, 默认是零点七五,这个词越小查询效率越高,同时也会导致数组的空间浪费越大。零点七五是一个比较合理的词,当我们数组使用率大于零点七五时,就会 触发哈西麦普的扩容操作。哈西麦普每一次扩容都是成倍扩容的,扩容后每个接点的数据都会重新计算存放的缩影值,扩容前所产生的电表红黑数也会重新计算,重新组合构造这个过程是非常耗时的, 如果我们在构造时就能给定一个合适的容量大小,就可以避免哈西麦普的多次扩容操作。有兴趣的可以看下圆码,欢迎随时交流。

哈西 map 和哈西 table 的区别以及呢底层实线是怎么样的?那么这道题呢,其实是一道呃, 时间比较久的题啊,就是说经常会问,对吧?但是说实话,这道题呢,已经比较的 out 了啊,为什么呢?因为这个哈西泰国了,已经不怎么用了啊,但是问这问这个题目的这个 意图啊,其实还是为了考察我们程序员这个知识面对不对,以及呢,我们对这个县城安全跟县城不安全这一块掌握到底怎么样啊? 最主要的其实还是基于这个底层实现,也就是说这个卡西 map 他这个底层到底是怎么实现的?前面的这个区别呢?其实很简单,对不对? 因为卡西迈普啊,跟卡西太伯他里面的方法其实都都差不多啊,而卡西迈普最主要的区别就是这个卡西特普里边的每一个方法都是加了一个生姜耐子的,都加了一个生姜耐子,而这个卡西迈普里边呢?没有, 所以呢,卡西太薄了,他是现成安全的,而哈西卖不是现成不安全的,也就是说当这种东西,你把它作为一个共享变量用 啊,放在我们的程序里面,作为一个共享变量,那么他在多线程环境下面,他的这个执行结果啊,这个卡西迈普他在多线程环境下面,他这个执行结果,那么呢,跟单线程执行结果呢?他是有可能会出现不一样,这个就叫做线程不安全啊,而哈西推国他不会啊,他不会。那么呢,那你多线程执行跟单线程执行呢? 他的结果肯定都是一样的,因为他里面的每一个方法都加了数啊,原因就这样,那么呢,所以呢,这个卡西太宝他的这个效率呢,很低啊,所以也是为什么我们现在呢不怎么用他的原因,而是用另外一个类叫坑卡人的卡西麦宝啊, 当然这个是后话啊,那么所以呢,回答这个问题啊,我们最主要的还是要基于这个底层实现啊,我们来看一下,好,前面呢,我简单的 写了一下啊,当然还有一个很很小的一个区别,就是卡西麦不能允许这个 k 和歪柳了,为闹哈西太婆不允许啊, 好,然后那个底层实现呢,这个是我们最重要的部分,那么这个卡西麦普的底层呢,实际上是采用数组加链表来实现的 啊,其实这个问题呢,呃,康飞老师,以前啊,在面试的时候,其实发现呢,有很多同学啊,在问到这个哈西迈步提成原理的时候,哎,数组加链表都知道,都能够打出来哎,但是仔细去问, 这个数组加链表到底是怎么回事,到底是数组还是链表,数组存什么?链表存什么?这个时候呢,很多人就懵逼了,不知道到底是怎么回事,就知道,呃,就会说一个数组加链表到底怎么回事呢?说不清啊,我们仔细来看一下, 那么这个 jdk 八呢,其实实际上呢,有一点点改变啊,就是在这个链表高度达到八的时候,数组长度超过六十四,链表呢就会转化为红黑数,这个点呢, 要注意一下,在 gdk 八的时候,在面试的时候回答这个问题的,最好把这个点提一下啊,展示了你对这个 gdk 的这个啊,这个升级了,是有这个关注在这里的啊,对,这个技术更新,对吧?保持一定的这个关注啊, 然后呢这个元素啊,在这个卡西麦内部是以这个漏的节点存在的,跟我们的那个拎格的历史人啊有点相像啊,就是唯 酷了一个,累不累,对吧?有一个漏的节点,那么这个漏的节点呢?实际上一看到这个漏的节点,我们就要想到列表,对不对?那么也就说元素在这个卡西迈普内部是以漏的节点存在的,然后这个漏的节点实际上就是我们的列表,对不对?哎,那这个数组在这里是干什么用的呢? 好,我们来看一下他纯元素的就是这个步骤啊,纯元素的这个步骤,首先呢他会计算一下这个 p 的一个哈气值,我们知道卡西麦虎最最明显的一个特征就是 py 纽结构,对不对?那么他是怎么样来做的啊?首先呢计算这个 k 的一个卡西值啊,就是一个卡西扣的,计算他的一个卡西值, 然后呢根据这个卡西值了,在二次卡西啊,记得这里呢有两次卡西啊,二次卡西。然后呢对数组的长度去取模,首先呢这个卡西面里面会维护一个数组,我们知道数组呢肯定有一个长度的,对不对?有一个长度,比如长度是十, 然后呢根据这个哈系扣的对这个数组的长度去进行取模,那么取完模之后,这个肯定呢就定对应到了一个数字,这个数字呢肯定比十要小,对不对?你比十要小,那么对应到的这个数字呢,就是这个数组的一个下标,对应到这个数组的下标啊, 那么如果说这个下标位置,这个数组的这个下标位置啊,我们来看一下,比如说这个数组啊,这个数组,如果说我计算出来的这个哈气扣的对应的这个下标位置,比如说是这个位, 那么这个时候呢,他就要判断一下这个位置上呢有没有元素,这个位置上有没有元素,如果说这个位置上是个空的,那么直接就放在这个位置,这个时候有链表,没有,没有链表,注意没有链表了,这个时候我就直接把这个元素往这里放 好,那么如果说我计算出的这个卡西扣的这个位置上已经有元素了,怎么办呢?这个位置上如果已经有元素了啊,那么这个时候才会出现列表,就是说产生了卡西冲突,意味着我这个地方呢,计算出来这个卡西值了,跟原来的这个元素的这个卡西值是一样的,对不对?因为你算到了同一个下标啊, 所以呢说明了产生的卡其中路。然后呢再进行一个一口子比较啊,看他这个一口子是否相同,对不对?那么如果一口子相同的话,意味着呢?我们 按照我们程序员这个实现的这个一口子方法啊,当然这个一口子如果你没有重写的话,那么就是双等比较,双等于号啊,如果你重写了, 按照你的逻辑来,那么如果说按照一口子比较,这两个元素如果是相同的话,意味着什么?意味着这两个元素呢?是同一个元素对不对?相等的元素啊,是一个相等的元素,他对指指向的同一个对象,那么这个时候呢就要取代该元素,也就是说这个 t 值 就是我们的哈西麦部的中的一个 t 值重复了,对不对? t 值重复了怎么办?取代直接覆盖啊,这也就是说卡西麦部里面为什么我们放一个相同的 t 值进去啊?这个外流值直接会覆盖,原因就在这里啊, 那么如果说要是不同的话,那么如果说这个一口子比较,哎,发现你卡西扣的虽然一样,但是我一口是来比较的,发现他的这两个对象呢不是相等的,那么这个时候怎么办呢?这个时候就出现了,我们这个列表出现了,列表 就在这个地方呢,维护一张链表啊,那么呢把这个元素呢往这个链表上面去放,这样啊,维护一个链表, 把这个元素呢放到这个链表上面,那么这个时候就是我们的哈西内部的这个数组加链表,那么然后当这个链表 越来越长,越来越长,也就说这个卡其中度越来越多,越来越多的时候,当这个链表呢达到了一定的高度的时候,我们知道这个链表的话,如果很长的话,他这个便利的 这个效率呢是非常之低的啊。那么我们在发现面部中我们如果去查询一个值,对吧?我们去 get 啊,去去 get 一个 k 的时候,那么 get 一个 k, 如果 get 到这个位置这个 k 呢?哎,发现这个列表上有这么多元素呢,我这个 k 到底对应的是哪个值呢?这个时候就需要去便利一个一个元素呢?去一口死比较,对不对?一个一个元素去一口 比较焊的到底是哪一个 k 好?焊的是到底是哪一个值啊?这个 k 到底是哪一个好,那么这个呢,如果列表很长很长的话,那么这个便利的这个效率呢,就相当之低啊,它是一个线性的对不对?一个线性的啊,那么这个时间复杂度呢,就 比较的高啊,这个时候就采用了红 k 树接地 k 八采用红 k 树,因为我们知道红 k 树了,是一颗平衡二叉树,对不对?平衡二叉树,那么他的这个茶型效率呢,是比较有保障的。那么这个时候呢,把这个链表呢啊, 换成一棵红黑树存在这个地方,这个时候链表就不存在了,不存在了,然后呢,这个这个地方呢,存的就是一个链表了啊,这个树主下边这个位置 存的是一个链表哎,存存的是一个红 k 数,存的是一个红 k 数啊,这个就是这个链表啊,就是变化成红 k 数的一个呃过程,当然呢,当这个红 k 数这个节点呢,数量逐渐减少之后,他呢又会退化成链表,又会退化成链表啊,他还会有一个退化的过程啊,所以呢,这个就是我们这个 卡西迈普的这个底层的这个原理啊,我们看一下长度低于六的时候啊,将红黑数转回列表就退化成列表啊,那么如果说 t 要是维纳的话,我们卡西迈普是可以 以存储这个 k 为闹的,值得,对不对?那么 k 要是为闹了,直接放在下标为零的位置,也就是我们在这个数组里面,这个下标为零的这个位置放置这个 k 为闹的值,那么这个呢就相当的方便啊,那么你这个 k 如果为闹的话,那么直接呢就可以取出来的啊,这个是比较方便的,因为这个闹了,我们 啊不太好去对他进行这个卡西扣的,对不对?不太好去进行这个闪电算法好,那么这个呢,主要就是这个卡西扣的底层的这个呃,实线啊, 当然他还有一个数组扩容,那么这个数组扩容呢,其实就跟我们这个阿瑞利斯特里面的这个数组扩容是一样的啊, 因为呢这个数组呢,在我们加瓦中数组是一个静态数组啊,这个数组呢,你一经定义,一经扭出来,扭了一个数组之后扭了一个数组,对吧?比如说我们又一个数组,那么这个数组的长度呢?就定死了,而我们知道这个卡西迈步呢,是可以就是一直往里面放直的,对不对?一直会 可以往里面放直,当然你不能超过这个,呃,最大长度啊,就是我们的这个硬头的最大值啊,那么如果说我这个数组一定义出来,那 那么长度定死了,这个数组是不可以再往里面放元素的,对不对?你再放元素呢就下标越界了,那么这个时候呢,就有一个叫做数组扩容机制,数组扩容是什么意思呢?就是说如果当这个数组长度只有十个,我现在呢要放第十一个元素怎么办?那么呢重新又一个数组,重新又一个数组啊, 又一个速度,然后呢把这个长度呢直接了变大,你产之前不是十吗?那么我现在比如说我给一个十五,或者说给一个二十,那么这个是 就基于这卡西面部底层的这个啊扩容因子啊,那么呢给他一个新的长度,然后呢把老的这个数组里面的数据呢,把他移到这个新的数据里面来啊,然后呢把你新的这个元素呢放到这个十一这个位置,可能这个就是数组扩容啊,这个呢?呃,那么以上呢, 就是这个卡西麦这个底层的一个插数据的一个过程,以及查数据的一个过程,以及到他底层破笼的这个原理啊,那么掌握了一项以以上知识的话,那么呢这卡西麦普跟卡西 table 这道题呢,应该是可以啊,做出一个比较满意的一个回答。

大家好,我是马武老师。吸盘爆满是我们经常遇到的问题,比如当前我的系统盘已经出现了红色警告,很多大型软件已经无法正常运行了。在这种情况下,我们首先想到的是清理磁盘空间, 当清理磁盘空间仍然无法满足需求时,这时大家多数想到的是重装系统,重新为吸盘划分一个大点的分区。这种方法虽然行的通,但需要备份文件,重装软件需要花费大量的时间。 其实还有一种更为高效,能从根本上解决吸盘空间不足的方法就是直接对吸盘进行扩容。如果你对此种方法不 熟悉或没有操作过,那么请看完本视频,本视频将传授你两种最为有效的吸盘控油方法,一是直接使用系统的磁盘管理工具,二是使用第三方的分区工具。 在继续观看本视频之前,反请大家给老师一键三连,以示支持。首先我给大家演示使用系统磁盘管理器对吸盘进行扩容的操作方法, 在此电脑上单击鼠标右键选择管理。接下来打开磁盘管理器,首先要做的就是找到系统吸盘的位置。 我电脑中的吸盘在磁盘一上,这块盘目前有四个分区,第一个分区一百兆是启动 分区,最后一个不到六百兆是恢复分区。这两个分区是安装系统时自动创建的,没有盘符,不用管它,我们看这个分区, 它的容量是三百七十多 g, 盘符是亿。而且在吸盘的后面,如果我们使用磁盘管理器对吸盘进行扩容,一定要满足这样的条件,就是在吸盘的后边要有空间, 这个空间可以是没有被分区的自由空间,也可以是已经分区的空间,比如当前的一盘。 对于已经分区的空间,我们首先要做的是对这个盘中有用的数据进行备份。打开此电脑,打开 e 盘, 由于 e 盘稍后我们要删除的,所以我们切记一定要先对这个磁盘上有用的数据做好备份。简单的方法是将这个磁盘下面所有的文件和文件夹都选上,然后直接将它复制到其他不动的磁盘上, 比如我这台机子, z 盘就是专门用来保存备份文件的,单击主页,单击全部选择,再单击复制。打开 z 盘,然后新建一个备份文件夹, 然后单击粘贴,就可以开始备份了。备份开始了,当备份完成之后,我们就可以开始对吸盘进行扩容 融了。备份完成了,接下来在 e 盘上单击鼠标右键,选择删除卷, 这时弹出了一个提示信息,意思是要求你对该盘上有用的数据先做好备份,否则的话这个盘上所有数据将被清除。单击式, 这时一盘原来所占的空间变成了未分配的空间,也称为自由空间,只要 c 盘的后边有这种未分配的空间,就可以对他进行扩容了。在 c 盘上单击鼠标右键,选择扩展卷, 单击下一页。现在我们可以设置扩充容量的大小了,这是目前系统盘所在磁盘的总空间, 这是目前可扩充的最大空间。如果你想将剩余空间全部分配给吸盘,直接单击下一页就可以了。如果你只想将其中的一部分划分给吸盘,可以直接输入容量,比如我想为吸盘扩充一百 g, 在这里输入一百,再加三个零,这里的单位是照自己幺零二四,照自己等于一计,我们在输入时没必要那样较真,直接按一千计算就可以了。单击下一页, 单击完成,吸盘扩充完成了,现在变成了一百七十二 g 多点。打开此电脑,我们可以看到吸盘红色的警告消失了, 现在剩余的空间有一百零二 g。 对于这个没有分配的空间,我们还可以具体创建一个新的分区,将它当做数据盘来用,还可将刚才备份的数据复制回来,这里我就不给大家演示了。 接下来我对使用系统磁盘工具对吸盘进行扩容做一个简单的总结。一、吸盘的后面要有未分配的空间,如果你的吸盘后面原来就有这样未使用的空间,使用这种方法是最为简单的了。 二、如果吸盘的后面的空间已经被使用了,就像刚才我操作的那样,需要将这个分区中的数据进行备份,然后将它删除才能对吸盘进行扩容,有点麻烦,如果备份的数据量 较大,也非常浪费时间。三、如果在吸盘的后面无法腾出未分配的空间或空间在吸盘的前面,此种方法就无能为力了。接下来我给大家演示使用第三方分区工具对吸盘进行扩容的方法。 吸盘已经被我恢复到了原来的状态,现在我就使用分区工具对它进行扩容。分区工具现在网上有很多,多数都可以对分区的容量进行调整, 现在我就以使用人数最多的磁盘分局工具磁盘精灵为例,我使用的是他的绿色免安装便携版,这是软件的压缩包,解压后即可使用,这是解压后的文件夹,这个 就是主程序,现在我就双击运行他软件被打开了。这个软件对磁盘分区显示的比较直观, 左侧这里是磁盘列表,右侧是分区明细,上边还有一个直观的示意图。首先是找到电脑中的吸盘,在这里在他的上面单击所有键,选择扩容分区, 这时我们需要选择一个给吸盘提供容量的分区,这个分区可以在吸盘的前面或后面, 对于我这个磁盘来说,由于吸盘的前面没有数据分区,所以只能选择他后面这个分区了。单击确定,这时 可以设置扩充的容量了,可以直接拖动中间的分割线, 这个是扩容后的总容量,我们也可以在这里直接输入容量,比如我输入二百 g b, 这里扩充的总容量是受 e 盘可腾出的巨大空间限制的。比如现在我输入五百 g, 变成了三百六十点八七 g b, 这就是可扩充的最大容量了。现在我还是输入二百 g b。 输入好之后单击开始 单击是再单击是在执行扩容期间,电脑会自动重启,并且会进入到 winpe 模式,这里的意思是处理完成之后是关机还是重启 windows。 由于对吸盘进行扩容,可能会导致大量的文件移动,需要的时间可能非常长。如果你选择睡觉时让电脑做这件事,可选择关机,否则的话选择重启 windows 就可以了。 设置好之后,接下来单击确定,我们等待最终的结果就行了。 处理完成了,电脑已经重启了,现在我们看一看吸盘,可用空间达到了二百零四 gb, 这就是扩容后的效果。执行扩容时,磁盘精英会在吸盘创建一个临时文件夹,就是它现在已经不需要 到了,可以直接将他删除。现在我就对使用磁盘精英对吸盘进行扩容的方法做一个简单的总结。使用这种方法,一、不管吸盘的前面还是后面,只要有空间都可以扩容。 二、在扩容时提供空间的分区,不需要删除文件,也不需要手动备份。当然了,如果空间不够,也可将上面的文件先移走。可见,使用瓷盘精英对吸盘进行扩容,总体来说在操作上更简单更方便。 当然了,如果吸盘的后面有未分配的空间,或者后边的这个磁盘文件不多,直接使用系统的磁盘工具进行扩容也是一种不错的方法。如果你还没有磁盘 精英这个工具,可以到这里去下载百度搜索计算机工具大全,然后打开这个网站,单击磁盘工具这个标签。在这里大家就可以找到一个磁盘精英的贴纸, 在贴纸的底部提供了百度网盘的下载地址。如果大家通过本视频学会了对吸盘的控制方法,请不要忘记给我点个赞,那么本视频到此就结束了,谢谢大家的观看。