00:00
那么关于咱们说这个类色接口这呢,有三个主要的这个实现类啊,这三个主要实现类呢,我们上午的话呢,就做了一个对比,诶这块呢,大家去熟悉一下他们三者的不同,那也就是说呢,大家如果面试答这个问题的话呢,就一方面呢,你先指出他们的这个相同点和不同点,那相同点这样一说,不同点主要呢关注于这个事情,那么接下来呢,其实还可以再去谈。啊,还可以再去谈,再谈什么呢?呃,再去涉及到我们这个ear list和link list呢,底层的一个源码分析了啊,也就是说呢,如果这个问题呢,只是把这块内容都讲完以后,其实呢,所有人基本上都会,当然也有同学呢,可能说的时候呢,这个这个说的又不熟了啊呃,这个这个呃,这里边可能也说不全啊,至少这里边这个点呢,按说呢,应该是比较基本的啊,大家都能够去表达出来啊,那只有有着这样的对比,你才知道开发当中我们到底应该用哪个类啊,这个你才能知道,那么接下来的呢,我们就来看一看,关于这里边比较重要的两个类,一个呢叫a list,一个呢叫link list,它们底层的一个源码分析啊,就像咱们前面讲string string buffer string builder,呃,咱们呢,也带着大家看了一下string buffer它的一个底层源码的一个分析,那这块呢,既然我们的集合也是容器,那么我们往里装数据的话呢,也会涉及到一个这个会被满啊,怎么扩容啊,诶这样的一些问题啊,通过这个呢,我们看源码大家就能看到我们所谓的啊叫动态数组。
01:22
说呢,你不用管这个俄到底这个这个会不会冒是吧,会不会这个容量不够,不用管,你只管往里放就行,呃只是说呢,这个呃扩容这些事儿,它底层帮我们做好了,不用我们去考虑了,但不意味着呢,它不需要扩容啊,这个数组的话呢,自始至终只要呢,你创建好它的长度就是确定的,那又因为呢,你的release底层用的就是数组,所以呢,同样会面对这样一个问题,就是呢,万一要是填的数据超了这个数组了,这个呢同样哎你得去扩容,只不过呢,不用我们自己去扩了,哎他呢帮我们把这个逻辑呢都写好了,其实那下边呢,咱们就不多说了啊,来看一下这个ear的一个源码分析。
02:05
Ara啊,List的一个源码分析,这个源码分析咱们呢,需要多说一下什么呢?就是这个啊,它在这个JDK7呃,和咱们这个JDK8这个版本当中呢,稍有不同。啊,有一些不同,那咱们现在呢,先以这个七的版本来说一下。啊,七的这个啊情况下啊,现在咱们来看一下这个七当中,这个情况是什么样子,然后呢,咱们再来看一下这个八呢,跟这个七呢有什么区别啊,相当于你也就知道它为什么要改了,那要是七的话呢,咱们现在添加的这个都是JDK1.8啊看这个七的代码呢,咱们就简单一点啊,我在这个给到大家这个资料当中呢,有一个JDK7啊,这个七的话,这个源码我就拿过来了啊这呢我们知道在Java的这个long包下,哎不是浪包YouTube包下,这呢我们就找到这个叫ear list了。
03:00
这呢就是我们这个七当中的一个源码,这样我就CTRLC一下,把它呢作为一个文件呢,粘到咱们当前这个啊mole下啊,那我在这个src下边去新建一个,新建一个package,我这呢不妨就叫做啊艾呃硅谷点SSRCSRC7啊好CTRLV一下。行,这样的话呢,我们就粘过来了,粘过来以后的话呢,这块呢,应该是会报一些这个错误的信息的啊,你看这块会报一些错误信息,这个我们就不管了,因为呢,毕竟你这个瑞呢,它涉及到一些副类啊,或者调用其他一些结构啊,这个就找不到了啊,这个我们就不管那个事儿了,主要呢,我们看一下这里边它的一个,呃,主体的一个源码的一个问题啊,首先呢,我们提到这个ear,不管是七也好,还是八也好,包括呢未来也好,它的底层的这个存储结构呢,哎,这是不会变的,就是我们一开始提到呢,它是用一个。数组来存的就是我们这个object类型的数组,这个呢首先明确一下,那么接下来我们关心的呢,就是呃,会调里边的一些方法,看看它怎么去做一些数据的添加,那么调方法之前首先关注的就是构造器,那就意味着我们得去创建当前的一个对象,那比较典型的一种创建方式就是new。
04:21
哎,就是这样的方式,我们去造个对象,那么我们就关注一下在这个七当中它的构造器,构造器我们先来看这个所谓的叫空三构造器,这呢叫a list,哎,我们就来看它。哎,这呢就看到了,那么这个瑞呢呢,它首先调了一下它本类当中重载的构造器,这次放了一个十。这呢,你点进去,这是一个十啊,小于零,这个不是就在这儿了,就把我们当前的这个底层的数组啊给初始化了,初始化的这个容量呢,就是十,刚才我们看到的在这个注释这块呢,其实也说清楚了,说呢构建了一个empty的一个list with an initial capacity ten。
05:05
也创建一个底层长度是十的object类型数组,诶看到这个的话呢,你其实就可以去联想咱们前面讲过的是不是string buffer啊,哎,Swim buffer底层它呢,当我们去new的时候呢,你用的空三构导器底层其实也帮我们造数组了,哎它呢是叫这个差类型的数组,哎叫Y6呗,哎它那个长度是多少来着,还记得对16,哎是16啊这个呢,它长度是十行,这是我们看这个源码呢看到的,哎咱们在这呢可以标识一下啊,在这种情况下呢,我们的底层,哎创建了诶长度是十的一个object类型数组。哎,这个数组呢,就叫做这个element data啊,我们在这写着呢,这个把这个。
06:00
CTRLC啊,这个我们就写到这吧,那么相当于我们造完这个对象以后,底层这个容器呢,其实这个容器本质上还是我们这个数组就帮我们造好了,那么接下来我们关心的就是往里添加数据。啊,然后我们ctrl o一下,咱们在讲collection的时候呢,已经说过添加操作叫做ADD,这呢仍然是叫ADD。好,我们现在呢,去做一个数据的添加,那这块呢,因为我们还没有讲泛型,所以这个E呢,大家就认为是object就可以了。首先呢,我们添加数据,先别着急往数组里边去加。那万一要你这个加的时候呢,超了怎么办呢?所以在加之前呢,先来确认一下我们的容量呢,是不是够,所以呢,有个叫in capacity啊eternal啊这样个方法,那这个size呢,是咱们已经添加了几个了。那对于我们初次爱的话,这个就是零,那这个size咱们这个看collection的时候其实也见过啊,就是我们调这个所谓的size方法,这个方法返回的就是你里边添加了几个,那目前呢,这个最初的时候这不是零嘛啊,然后呢,我们这块呢,也没加过,所以这就是零,如果我们是第一次添加,那就相当于你目前要加个元素啊,就是零加一啊,这就是个一有个印象啊。
07:13
嗯,我们先点进去看这个变量呢,我们先不用管,这涉及到一个叫快速失败机制了啊,我们先不关心啊,这呢是一个mean capacity啊,就零加一过来了,那么这呢,你这个值和我们本身底层的数组这个长度呢比较一下。啊,你像咱们要第一次添加这个数,肯定是这个小,所以呢,这一定是一个负数是吧。哎,你要是个负数的话呢,这个就不会涉及到这个所谓的扩容的问题了,哎,所以说我们就会回过来,哎继续呢这呢做一个添加操作。那么这样添加添加添加添加我们这儿呢,使用的默认构造器长度是十,那如果说我们现在已经加到十个了。就已经满了啊,当我们加第11个元素的时候,这就是十加11了,点进来点进来的话呢,这是11 11减去十大于零,相当于就是这时候不够了,不够呢,我们就就需要去扩容,将我们这个min capacity你放进去啊,勾一下这是11,那么我们看下这个扩容的一个比例。
08:17
这个首先呢,记录一下你本身底层数组的一个长度,叫older capacity。然后的话呢,诶,我们这个新的这个capacity容量呢,就是O的加上一个它。向右移一位,相当于是除以二是吧,那就相当于我们默认情况下,容量呢,扩容为原来的1.5倍吧,诶默认呢,就扩容为原来的1.5倍,这个跟咱们string不太一样。所以ma咱们应该是扩容为两倍,再加个二是吧,诶对的,这呢就扩容为1.5倍了,哎,扩容完以后的话,这是一般情况啊,有的时候还会有一些特殊情况,比如说你这个扩容完以后呢,跟你现在实际这个容量需求发现还是小的,还不够不够,那就干脆拿你这个容量得了啊就这个意思,那后边呢,还有涉及到就是比较少的情况会出现呢,就是这个呃,加加加一快加到我们这个整形的这个最大的这个值的情况了啊,那如果是加到最大值这个其实它设置了一个,这还不是算我们这个整形的最大值啊,这呢,我们提前设置了一个值,如果你比它还大的话呢,这个呢就会取这里边啊,相当于取我们这是整形的这个最大值了啊,就是这个数呢,就不能再打了啊,那还还不靠谱,这就开始memory error了啊啊,这是一些这个特殊的情况了啊,那我们主要关心的就是这个一般的情况就是扩容为原来容量的1.5倍,那扩容完以后扩容就完事了啊,相当于原来这个数度长度是十啊,你现在做一个扩容,扩容完以后呢,你。
09:47
才需要将原来的数据呢给它copy过来,所以下边呢,你看这个element data,就是这原来我们这个数组叫element data,它呢需要重新被复制为这个数组。哎,我们还需要将原来数组当中一个元素呢,给它copy过来。
10:02
那这呢,就是我们看到底层的一个源码。好,回过来我们把这个呢稍微说一下啊,这呢我们是造了一个a list,那么接下来我们这个list啊,点调一个爱的方法,我这比如说就写个123,它其实呢,就相当于我们这个叫element data这个零的位置呢,哎,负了一个值叫123。当然准确的说呢,你这个位置其实是相当于是一个阴记者了啊。哎,是这么着一个情况,行是相当于这么着一个情况啊这呢我们就做了一个添加加加加,然后呢,我们提到了说list.add啊加了一个,比如说这个11吧,呃就是呃当呃添加到呃当这个新要添加的。呃,添加的这个元素的这个个数哈,嗯,或者当心药应该这样说吧,嗯,如果这个此次的添加。
11:01
哎,导致这个底层呃,Element data这个数组容量不够,哎我们说则扩容啊扩容然后呢,默认,哎默认情况下啊,扩容为原来的呃容量的呃1.5倍。嗯,相当于我们就新造了一个数组。新造了一个数组,然后呢,这个是原来长度的1.5倍,那么你这个新造完以后啊,我们说这个同时啊,需要将原有数组中的这个数据呢,Copy到新的数组中。哎,数据啊,然后复制到这个新的这个数组中。哎,然后这个新的数组呢,就作为我们这个,呃,目前这个element data塔的一个新值出现了,呃,这样的话呢,就完成一个扩容,然后对于我们普通的这个,呃,这个底层源码,我们看到是一波开发的人啊,咱们呢,相当于人家的这个使用者啊,那么对于咱们这些使用者来讲呢,大家其实感觉不到说这个底层做了什么事,我们呢啊,只管一直往里边去艾就完了。
12:14
那实际上呢,我们会看到啊,假设呢,大家现在需要往底层呢,呃,需要往俄中存50个数啊,你就一直去艾,那其实你会看到长度是十啊,不够了,这个1.5倍是15了是吧?呃,15的话呢,又不够了,又去扩容啊,又不够了又去扩容,其实里边呢,会经过呃一些这个不断的去啊,抛弃旧的,创建新的这样一个过程。啊,类似于咱们前面讲的string buffer,或者准确说应该叫string builder了啊,因为它线程不安全嘛,哎,跟那个string builder其实是一样的啊,哎,就是都是这样的一个复制的一个过程啊,那么通过咱们刚才看这样一个源码对我们的启示是什么呢?哎,其示的话呢,就是咱们在开发当中,如果大家基本上确定a release当中你大概要放多少数据,所以我们推荐呢,大家不要用这个空三的构造器啊,结论呢,就是建议开发中呃去使用。
13:12
啊,使用了这个,呃,代餐的这个构造器,那哎构造器啊,就是我们这个release呢,那它还有一个,诶用一个叫a release,这里边呢,我们可以写一个int型的叫capacity容量啊看一下我们这个API ctrl一下啊a RA list,你看这有个它哈,点开这个呢叫initial capacity,那么这个值呢,就决定了我们底层创建数组的这个长度。比如说大概你知道呢,我们这个release当中大概要放50个啊,或者说不超过50个,那你此时呢,就调这个构造器,直接呢,把这个50放到这儿,数组的长度就是50,会避免在中间环节去扩容。
14:00
哎,这样呢,就效率呢,稍微要高一些,这个都清楚是吧?好,这是我们说的这个JDK7当中,哎,我们的初始的情况,包括呢,后边扩容的一个情况,行,那在这个基础之上,我们下边来看一看这个JDK8当中。哎,这个八当中,我们这个release的一些变化。哎,它的一些这个变化啊,嗯,这个我写成一个这样吧,这写一个叫一啊。叫list的一个接口框架。啊,这呢,我写成是一个二啊,它的一个源码分析,哎,这个是在JK7的这个情况下,2.1。2.2在这个JDK8当中,A list的一个变化是什么呢?这个我们直接呢,Ctrl shift t在这呢,我输入一下叫a list,点开这个勾啊,我们看下这个ul项,这时候咱们看的就是1.8当中的了,那么1.8中当中我们也是先来看构造器ctrl o找a list。
15:06
我们还是通过这个空单的构造器呢去造对象,那进来以后呢,发现代码确实不一样了。那此时我们在new一个对象的时候呢,这点element data就是我们底层的一开始这个数组哈,一开始也是一个no,然后在构造器当中,它指定了一个常量,这个常量呢,你点一下它长这个样子。啊,长这个样子,相当于这里边儿什么也没有,所以不会像我们七一样,在你造对象的时候呢,就把这个底层的数组呢实例化了,它呢并没有。嗯,那从这个创建的这个,呃,这个这个时间上来讲,或者说你一开始就占用很大的这个内存上来讲,这种方式是不是要好一些,对吧,就我们这时候呢,造这个对象,其实底层那个数组没有给你创建,相当于我们这时候内存呢,不就节省下来了吗?嗯,那么他什么时候去创建呢?对接下来我们就得说这个I的操作了。
16:00
艾特操作,哎进来以后你看这块呢,还是有点像的哈,诶这个首先呢,也先确认一下这个容量够不够,哎我们点进来,点进来以后这块呢就有区别了,它呢首先看一下啊,说你这个比如说像咱们首次掉A的这时候呢,你的element data不就是咱们刚才设置的这个值吗?所以呢,你要是第一次添加它就判断一下你这个当前的element data是不是这个值呢?啊是啊,如果要是的话呢,这呢就哎又提供了一个这个meanin capacity,让他是谁呢?这个提到了一个叫default capacity啊,就是咱们这个十倍。啊,而你这个min capacity,咱们第一次就是零加一嘛,所以这两个取最大值肯定就是十了。哎,这个十呢,就出现了,出现十以后,这有这个方法,把这个十放进来,哎在这里边,哎这个因为这是十了,这个一开始这个lengths,这不是这它一开始不是长这个样子吗?Lengths不就是零呗,哎,零,然后这个就是十减零,这不就是十嘛,大于零,大于零,我就把十又搁进去格一下,哎过来,然后element date element.length这不是一开始的时候。
17:10
诶,我们看啊,嗯,这是一开始的时候,还是一开始这个是不是还是这个零啊,嗯,还是这个零,嗯嗯还是这个零,然后这块呢,零这是一个它。嗯,这是零,嗯,零加上这个这个呢是一个新的,嗯,零这这块1万以后呢,这块还是零呗,然后这个相当于零减去这个十,零减去十还是个负的,负的,然后这块呢,把它付给它,这个就是十了吧。这个是十,然后呢,这块呢没进去,它这不就把这个新的这个数组就造好了啊,这不就是长度十就出来了,有点绕是吧?哎,那么总结一下呢,就是相当于咱们第一次在调I的方法的时候,才帮我们把这个呃数组呢给它创建好了,哎创建好了,那创建好以后在后续你再去做艾艾艾,那包括呢扩容,其实都是回到这个代码,这个代码呢,跟咱们刚才看七里边呢,其实是基本一致的,就是刚才的这块的一个操作。
18:11
哎,在这块这个操作是不一样子的了,哎,包括构造器当中是不一样的了,哎把这个呢,我们总结一下。在JDK8当中的变化就是我们通过这样的方式呢,去创建一个诶a list通过这样方式创建,此时呢,我们说底层。哎,底层呢,这个element data,它的初始化为。啊,底层object类型的这个数组,阿里啊初始化为这个。啊,意味着就是并没有创建长度为十的数组,哎,并没有创建啊,那你看他这个API呢,咋写的。Ctrl o一下这是吧。
19:00
你看他是咋写的。说呃,创建了一个empty list啊,有一个初始化容量为ten,这是不是有点骗人呢?哎,对,就是JK8当中呢,把代码改了,然后注解没改,注释没改是吧?哎,这时候呢,其实并没有帮我们去创建啊,或者换句话说呢,就是他就这样写了,嗯,没啥大的毛病,就是你爱的时候呢,是不是还是有啊,哎,它在艾的时候呢,就体现出来,我确实给你造这个石了啊,其实呢,只是没有在构造器当中去造啊。嗯,再回过来,这是我们说的这个事儿,然后呢,当我们这个通过这个list点艾特方法,第一次呢,调我们这个艾方法时说第一次,哎,调用添加操作。哎,这个时候呢,哎底层才创建了长度为呃十的这个呃数组,哎,然后呢,并将咱们这个数据啊123呢添加到。
20:00
哎,添加到什么呢,我们这个,哎limit特地达呢,它的第一个位置上。哎,添加到这儿行,然后呢,后续这个操作呢,与我们上边呢,JDK7当中又是一样了,哎后续的添加,哎和这个扩容操作,呃,与JDK7啊无异。哎,这样就结束了,那这样的话呢,我们把这个JDK7和八当中release这个源码呢,就说清楚了,那主要呢,就集中在一开始的这个时候不一样啊,那么做一个简单的小结。啊,这个小结啊,小结呢,就是一开始这样,后来改成这样,那你想一下他为什么要这样改。哎,图什么啊,或者大家去说你说这种好还是这种好呢?对,其实呢,相当于这种呢,要稍微好一些了,我们造完一个以后呢,呃,比如说咱们一开始在这个,嗯,后台这个层面吧,咱们造了一个,接着你要往里装数据,但是这个数据的话呢,你可能需要去数据库中请求一下,你请求是不是需要点时间。
21:12
啊,或者再换句话,你在这个安卓的这个浏览安卓的这个客户端这块,你造了一个list啊,现在在等着去装这个页面中的这个数据呢,那现在造完以后呢,你需要这个网络访问,网络访问完以后呢,你才能够回过来把这个数据显示回来,这呢也需要时间,那你这个时间之内呢,诶七当中提前就造好了,八当中呢,诶先不着急造。啊,这呢,就是体现了这个内存呢,就节省了。啊就节省了,所以呢,这时呢,其实可以做个类比啊,就是咱们这个七当中的release,有点像咱们前面讲单利模式当中的。懒汉还是恶汉,对它有点像恶汉式,就是一上来呢就帮你造好,而这个八呢就是哎,相当于改成是个懒焊式的,一开始没有造,当你需要的时候呢,我才造,诶,它的好处就是延迟了这个数组的创建过程啊,创建这个时间啊,节省内存啊。
22:07
嗯,我们小结呢,就是JDK7中的啊a release啊,这个它的这个创建啊,啊类似于。哎,类似于我们说单立的这个恶函式啊,那而我们JDK8中的release它呢,嗯的这个创建啊。诶,准确说应该是他的对象的创建。那它的对象的这个创建。哎,这个呢,哎类似于哎单立的懒汉式啊,延迟了啊,这个数组的一个创建啊,然后呢,相当于节省这个内存。
23:02
好,这样的话呢,我们把这个这个源码分析呢,就说清楚了,哎,大家呢,如果去答这个问题的话呢,你可以就知道啊,这个呢,应该这样去说啊,这呢就是它的一个源码分析。
我来说两句