00:00
好,那么string说完之后呢,咱们这个字符串呢,还不算完事了,接着呢再讲两个跟string相关的两个类,一个呢叫做string buffer,一个呢叫string builder。那为什么要讲这俩呢,这块呢,我们就要对这三个类呢做一个对比了,这儿呢,也是我们比较常见的笔试和面试题啊,我们在这说一下,哎,那我们已经讲了是string了,接着呢,我们再看一个叫string。先说这个叫buffer。先写它这个呢,就按照我们这个GDK呢,声明的先后顺序来了啊,然后后边呢,某一个版本当中出来的这个死build啊。好,那么这三个类有什么区别,咱们上来呢,就把这个事儿说清楚,此类呢,这个呢,我们说呢叫不可变的。字符序列。啊,那么下边这两个呢。都叫做可变的字符序列了。注意啊,这块有一个这样的区别。通过这呢,当然我们也能看到哈,为什么说他们仨有什么区别呢?就这三个呢,实际上都是表达的跟字符串相关的。
01:07
也是我们在开发当中,凡是你需要写的一些文本数据,都是用这三个类的,这三类的区别呢,就是先把论画出来,它是不可变的,就是我们所谓的什么呀,诶咱们比如说把这个数据呢,先都放在这个字符串常量池里边了。然后呢,现在呢,有好几个字串呢,用的都是hello,比如说那么大家呢,就共享这一个hello。这个哈呢,体现为呢叫不可变的。就凡是呢,你比如这叫S1,这叫S2,这叫S3,不管你们里边这个谁现在想去改这个哈呢,那不好意思,你都去重新再造一个改是吧。这个造的这个呢,你要是自变量的方式,那你就在常量池里边,要是通过new的方式呢,你就放到堆里边。或者你说呢,去replace啊,或者这个连接拼接呀,那都是相当于是new的方式。那就放到堆里边了,总之凡是呢,谁想对他改都改不了。都必须呢,先去造一个这个体现为呢,就是这个结构呢,叫不可变的嘛。
02:04
对吧。那对应的,你说什么叫可变呢?可变对杨爱之的你比如这个哈,我想后边补一个ABC。他就可以在,你就不能在这说了啊,是不是这块有一个相当于一个哈,是吧,我想补个ABC是不是就可以直接还在原有的这个位置补一下啊。哎,这就好比是我们这个是叫。对,价格比了buffer也好也好是吧,他俩的话呢,一开始叫,然后现在呢,又拼了个ABC,还是用的这个。然后呢,后边补了个ABC,这叫做可变。注意,这叫可变啊。有同学别整反了,什么叫整反了,说你看你这个啊,这个是它,然后呢把它呢,想加个ABC得重来一个,这个呢,加个ABC还在这块呢添加的,说这不叫不可变吗。没变,这个呢,你看又画了一个,这不叫可变吗?反啦。
03:00
那还不如不说呢,是吧?就就是你别整反了哈,就是这个,你看我们在原有的位置改的,有同学说这不叫不可变吗?这个地儿没变呀,啊这个呢,你看你改的话呢,这不就换个地儿,这不地儿变了,这不叫可变吗。不是这个意思啊,是说呢,你这个结构能不能变,这个结构呢,不能变,所以心去造这个结构能不变,能变,所以呢,就写这儿了。就是理解的角度啊,别给整错了啊。好。回来啊,那么这哥俩呢,都是可变的,那你俩有什么区别?是吧,这块我们就提到了这个。八是吧,它呢是这个JDK1.0的时候呢,声明的啊,这个叫。声明的。它的这个特点的话呢,它是线程安全的。这个线制安全,为什么我们先讲线程,主要是就这个原因,当然大家呢,一开始一出了面向对象就讲线程,感觉稍微有点难是吧?当然我们先讲的目的呢,就后边呢,我们讲这个string bar build,包括讲集合的时候会更方便一点,因为有的安全有的不安全,一说呢,大家就知道什么意思了啊,而这个string builder呢,它是GDK5.0时候声明的。
04:10
那它的主要特点就是线程不安全。那线程安全呢,它对应的跟这个不安全比呢,它就效率呢,稍微的要低一些是吧,这个咱们是相较于下边这个来讲的啊。哎,它呢,就是效率呢要高一些。哎,就是有这样的一个区别。啊,这个注意一下,所以这呢高低指的是它俩,它俩呢跟这个string比呢,那要涉及到这种这个增删改的操作,都比这个string的效率要高啊,只是它俩之间的一个特点。也就是说呢,这个string buffer呢,一开始的时候我们只考虑这个,呃叫安全啊,数据呢要正确,但是其实呢,我们在很多场景下呢,都不涉及到多线程的问题了,那么就不推荐你用string打火了,你就用string builder就可以了。这个安全不安全呢,咱们CTRLC看一下这个源码呢,也非常容易的就看出来啊,热带这1.8里边儿往下找这些方法,你看是不是都加了,这叫C空子了。
05:03
哎,这就说明它是这种安全的哈,那对应的我们在CTR,咱们来一个叫string builder啊。哎,就它了啊,点进来这个呢,你看它这个方法的话,你看就没有这个使用寨子的修饰了。所以呢,非常简单的区别就是有没有用CI去修饰来体现这个安全和不安全。它俩的区别就是这个。好,那有没有共性呢?实际上也有,他们都是跟字符相关的啊,都叫做字符串了,这个叫不可变的字符串,这个叫可变的了,也就是说呢,他们这个底层是一样的啊。对于我们这个来讲呢,底层呢,咱们说使用这个差赢数组这个呢,你得写个前提是JDK。八的时候是吧。A等于八之前。啊,因为GDK9的时候呢,我们说变成数组了是吧。哎,那么下边这哥俩呢,一样的道理啊,他们底层呢,在GD8级之前呢,也是用的差数组,到GD9以后呢,它改成BY的数组了,他俩呢也变了啊。
06:03
这个我们也可以在这儿多写一句。李沧。啊,使用。这个呢叫BAT数组。它呢是JDK9之后。啊,那下边这个呢,也是一样的,也就是说在这个方面呢,他们底层的结构上呢,大家是完全相同的。没问题。好,那么这个事儿呢,我们就说完了啊,为什么要讲他这哥俩啊,这个呢,我们也说清楚了,那大家呢,可能就会接着有一个问题了,什么问题呢,说呢,他们底层使用的结构啊,你看都是一样的。一个变一个也跟着都变啊,那为什么结构都一样表现出来的他俩呢,就叫做可变了呢。同步不同步呢,这个咱们刚才看了,就是加没加寨的一个区别了。那也就是说这俩里边呢,我们可以挑一个,比如说就以build为例吧,这个string build呢叫可变的了,呃,这个叫不可变的,这个怎么体现。它这种可变和不可变的,因为底层结构看都一样嘛。
07:04
对这块呢,我们就可以给大家说一下这个源码的问题了。来我们看一看啊,数列源码的话呢,首先我先说一下这个string。此类的话呢,比如我们这样的方式,我创建这个对象,它其实底层呢,咱们这个叉型的这个数组。哎,又一个差异速度相当于这个长度呢,就是零。那如果说我这样写的呢,哎,我一个string,这里边我写的叫ABC。啊,那就相当于我们底层这个差型数组。这个长度呢,就是三。然后呢,这个整个大括号。这就是A。B。C,那就是这样子的。啊,就是这样个场景,好,那么它是这样场景,那对于我们这个string buffer和string builder来讲,它呢,就不是这样子了,包括呢,我们当时看这个的时候呢,还发现了他这个差型数组,它的声明是一个。Final的是吧。
08:00
啊,这不,我们当时看这个源码的时候在这儿啊,底层的这个Y6数组是不是还是个final修饰的吗。好,那对于我们这个buffer和builder,注意这俩的主要区别就是加没加这个袋了啊,所以这块我就以这个builder为例呢,就是做一个说明。OK,这个呢,是我们先相当于一个回顾吧,哎,针对于死来讲。下边呢,我们说针对于咱就说呢,叫string builder来说。好对对他来说呢,首先CTRLN一下string build,我们就看这个八的吧,来进这个源码。这个在它这个结构里边呢,我们说了底层你不是也使用这个差异型数组吗?咱们看的是1.8的圆嘛啊。你要使用差异数组呢,先把这个数组呢,先找到看看在哪呢。诶,CTRL f12这块呢,我们找一下这个所谓的属性这块,你发现呢,在这没有是吧。这个呢,是因为它没有定义在当前这个类里边,而是放到了它的父类里边了。
09:03
这个负类呢,叫做abstractstream builder是吧?叫这个名,这个我在这儿呢,CTRLH一下。这面就是abstractbu吧?你会发现呢,这个哥们儿呢,有两个子类。一个呢叫string buffer,一个呢叫string builder,相当于他俩父类呢是一样的。那它俩的主要区别呢,其实方法都没有区别啊,主要区别就是有一个加ne,一个没加,这个核心本质问题,大家注意一下就行了,好,那因为呢,它俩都是表示可变的,所以这块呢,关于底层这个数组啊,干脆都一样嘛,那就放到负类里边了,是这个思路,所以呢,我们找这个负类。这儿呢,我们就找到这样一个数组了。这个数组你发现了它没有final修饰是吧?其实呢,就在同一个对象的情况下呢,这个值是不是可以地址可以改吧。这不就体现了,就是你可以换呗,是吧,哎,这呢,就我们发现呢,它底层对应的这个属性啊,一个是它还有一个呢,叫count。
10:02
好,对于他来说,呃,内部的属性有啊有俩一个呢,是它一个呢是int型的,刚才我们看到有个count。啊,这个呢,就是来存储。咱们说呢,这个字符序列的啊。说你这个buffer你叫hello啊,你叫ABC啊,其实都存到这儿了,类似于我们里边那个Y6,然后这个count呢。这个炕呢,就像咱们前边写项目当中,当时咱们这个银行,比如说是吧,银行里边呢,一开始这个客户的这个数组长度是十,实际上呢,我们就存了仨,我们有专门的一个number。呃,这个customers是吧,记录一下说实际存了几个或者叫total,呃,然后我们这个total呢,就相当于这里边count,就是你实际存储的字符的个数。这个呢,在咱们这个string当中是不存在的,因为呢,我们这个string的话呢,它有个方法叫Les。这个LS呢,获取的实际上是我们这个数组的长度。
11:02
数组的长度就是我们这个字符串的长度。比如我这呢,我这个字符串叫ABC,那这个数度呢就是ABC,那这个数数度呢就是AB和C长度就是三,哎哟,我这怎么还写错了呢,是吧。就是三。失误了是吧。诶,就是三啊,所以呢,不会出现在这个数组的长度和我们这个字符串里边这个字符的长度不一致的情况。Uff。如果我这样的方式。我叫S。S8。哎,叫它是吧,好,我们一个string build这个位置呢,我是这样写的,有点类似于呢,像我们这样一个构造,那么它底层是什么样的呢。哎,咱们看一下源码。这块我们看这个string build了啊,CTRL f12。
12:02
就看这个呗。然后你看这儿。这个16是吧,看这个声明也行,说构建一个string builder,它呢没有任何的字符在里边。很显然,你也没有提供一些字符的数据是吧,但是呢,它有一个初始化的容量。是16个字符是吧。哎,那这个16呢,放到哪呢一点,这个16放到这儿,诶看一下。是不是比较清楚啊。哎,那这个呢,相当于怎么着呢,本质上来讲,回过来我们这个写法呢,其实它底层呢,创建了一个Y6数组,这个数组呢,不像我们上面是这样写的了,它呢是拗了一个叉形数组,长度呢是不是16啊。只不过呢,这16个里边呢,一个也没放是吧。所以呢,我们这个抗呢,其实是零。但是这个数组的话,你注意数组的长度是16啊。对是这样子的,哎,那顺带呢,咱们再看一个方式。比如说啊CTRLC,相较于咱们上面这个呢,我在这儿呢,也可以写上一个ABC。
13:05
啊,那么这个它长什么样呢?我们再看一下string build。嗯,类似于,诶是不是就这个。我在这呢,写了个ABC嘛。基本上你也能猜出来了。就是在你现这个字块的基础之上呢,我再空出来16个。哎,就成这样了,相当于呢,回过来,哎,这里边儿是怎么创建呢,就是诶。CTRLC一下这个16的基础上呢,再加上你这个相当于是ABC它的一个length。是不是这意思了?相当于它始终呢,保证我这儿呢,有16个是不是空的呀。这个空的来干什么用呢,方便你后边呢去添加呀,比如说我拿着当前这个对象啊,过来点这块添加的话,它有个方法叫openend啊,比如这里边儿我加了一个小A。这个操作呢,就相当于在我们这个数组角标零的位置呢,给你复制为一个小A了。
14:05
就相当于是这个,然后呢,接着呢,我们再添加一个B。这个B呢,它就相当于是在你这个后边这个位置,这个叫B了。在添加的时候呢,这个count呢,也跟着这块呢,就加加了一下,这块呢,也加加了一下,这就有两。啊,那如果我这块呢,我要写。写AC呢?那就相当于这个位置完了以后呢,接着Y61它呢,接着这块呢,是不是叫C了。啊,那这个再加个B这块相当于就二呗。哎,就这个意思。那怎么叫可变呢?这不就说完了吗?我们原来string的时候呢,只要你稍微做一点变化,我这已经定死了,不能变了。你在你说嗯,这比如我这块呢,String的基础上,咱们来加上一个D是吧,大紧注意啊,然后加个D,加D的话,你注意这块不能加了,因为数组呢,长度是不是确定的呀。
15:01
那你得新造一个是吧,新造一个你说ABC叫D了,我还用这个Y6行不行,六是不是final。反正不能动。Final不能动,你看影响了什么?我们string里边有个属性叫value。这个Y6呢,现在呢,肯定是要变成它了,但是呢,Y6不能动。Y6不能动,那只能动对象了。因为想想你要还用这个对象,对象这个属性已经翻住了,地址不能改是吧,所以呢,现在往回推只能是改这个对象了,所以相当于我们这呢,是不是得重新创建一个对象,那你对应的这个数组自然而然也是新的了,这不就我们所谓的你不能在原有的基础上改,你得新造一个原有的这个呢,是不可变的嘛。那对于string buffer来讲的话呢,我们还是这个的数组。啊,这个数组呢,现在我们在这里边儿呢,就一个个往里边去加,你还是用的这个数组,所以呢,它是一个叫可变的呀。包括呢,这个本身还没加final是吧。那影响是什么呢?
16:02
可以换地址是吧。这话低纸是因为什么呢?我现在换地址了吗?没有是吧,因为我现在用不着换,我什么时候就需要换了呢。对,我不断的添加。那对于我们当前这个操作来讲的话呢,它最多是不是就放16个字符了是吧,那你加加加加加第17个的时候呢,这个时候其实就有问题了,我们现有的盛不下了,盛不下的话呢,怎么办呢。那就要新创建一个数组是吧,新创建一个数组,你注意哈,如果我这个Y6要是加final了。意味着你又不能是在心脏,这个速度就不能还给Y6复制了。那当然又想改,怎么办呢?那只能是重新新造对象了是吧,你看这块呢,其实又体现有点还是有点那个不可变的特点了,所以这块根本就没有烦,就是说你还用这个对象。啊,还有那个对象就行,然后里边这个数组的地址变一下就可以了,这个对象还没有变。
17:05
啊,提前这个事儿就行啊,不断添加,添加的话呢,这个我们说啊,一旦啊。啊,一旦啊要添加的,哎,应该是这样说吧,一旦咱们这个count是吧,它不是表示你实际的个数吗。啊,一旦这个count呢,要超过。嗯,超过啊,超过了咱们这个叫Y6.lengths时啊。就需要。扩容。呃,那么这个扩容的话呢,我们稍微看一看它是怎么来处理这个问题的,那我们现在呢,只需要呢,就看一下这个喷的这个操作了,因为它在添加的时候呢,它会有这个考虑啊,那我们就看虚这个喷的。哎,西边也判断的时候呢,这是空的问题,咱们暂时不考虑了,哎这块是添加,举个例子,比如呢,我们现在就使用它这个默认的容量,假如就16了啊,现在我们已经添加了,假如14个了。Count,目前假设我们就是14了啊,然后现在我要添加一个ABC。
18:03
这个呢,是不是长度是三啊。那14加个三就17 17大于16,相当于乘不下了嘛,所以呢,先把你这个ABC的长度获取出来,这就是。三是吧,然后呢,现有的这个count呢是十四十四加上个三,这个方法呢,就是来判断是不是需要扩容的。所以呢,14加个三好,我们就点进来。点进来这个呢,不就是14加三就17吧。哎,判断一下它呢,减去这个本身Y6.las,看是不是大于零,咱们这是17,这个是16嘛,大于零说白就不够呗。不够这块呢,我们就开始这是一个复制操作,主要就看这个位置啊,把这个时期呢进去有个new capacity新的一个容量。新的容量呢,我们看是怎么扩容的呢,它默认的一个标准。对,就是原有的这个数组的长度呢,左移一位。这是咱们说到的在底层会看到的微云算伏的位置了,这相当于是不是乘以二啊。
19:00
然后呢,再加二是吧,比如说呢,这个默认的这个容量呢,就扩容为原来的一倍呢,再加个二,这就我们新的容量。但是这块它还有一些细节啊,细节的话就是万一你心内的容量还是不够呢。你比如说我我们现在这个这个是16是吧,现在已经有14个了,现在你突然写了一个特别长的一个字符串。特别长呢,导致的话,你这个16呢,乘以二再加个二呢,这也就34是吧。那万一你这个特别长的假设50个字符呢,这一加是不是你这个扩容完以后还不够。那这块我们干脆你不够,那我就用你实际需要多少就给你多少得了。啊,就跟大家你比如说呢,你找你爸要钱是吧,诶像小时候呢,那时候钱感觉挺值钱的啊,我记得五块钱都能买好多东西。啊,你比如说找我爸要的,我买个东西,他说给你五块钱。然后这时候我也告诉他说不够不够他这块呢,就默认的,比如说来个五乘二加个二,说那我给你12。我说还不够。这时候下一步是什么呢?
20:01
该被打了是吧?诶下一步的时候他会问说你到底需要多少,我说需要20,那我就给你20吧,诶意思就是这个。啊,就这个啊。行,当然这块还有一些具体的限制,这个涉及到最大值的问题,就是因为有可能移的时候呢,这块会变成负数啊等等,这个咱们暂且就先不多说了啊,诶回过去也就是说呢,我们看到了就需要扩容,那么默认诶扩容为原有容量的二倍,再加个二是吧。诶,这个广控容还不行,咱们刚才在这源码里边,其实还有它前面那个逻辑就涉及到了有那个copy of。那点进来。然后在这个位置点进来是不是有这个。哎,就是你不光要扩容原有的这个数组的元素呢,在扩容完以后,是不是还得要复制过来。诶,这个呢,也别丢了啊,扩容为原来二倍,然后呢,诶,并将原有Y6数组中的元素呢。哎,元素是吧,哎复制到哎新的这个,诶数组中。
21:05
当然了,如果呢,我们这个数组啊,对咱这是差了啊,差的话呢,您直接呢,把这个数据呢,就复制过来了。像正常我们讲一个数组的要复制,有的时候呢,他那是引用类型的,其实光复制个地址是吧。那就可以了啊好,这个呢,就是我们说的它整个这样的一个结构了。啊,这就说完了啊,那说这个的意义是什么呢?就是在我们实际开发当中,大家看一看,针对于字符串这块,我们有三个类可以去选择,那我们该如何去选呢?是吧,诶这块呢,就是给我们的这个启示的,那我这样说一下啊,说呢,诶如果开发中。啊,开发中需要呢,频繁的啊,针对于字符串啊进行,比如说这种啊增。哎,山。诶改是吧,哎等操作。那就意味着我们这个字符串呢,得老变这个内容啊,那由于呢,这个死率呢,是不是不可变的呀。
22:02
哎,那么如果你要有频繁的这个操作的话呢,我们说建议呢。哎,是不是使用这个string的。哎,八份不是或者是用这个string builder是不是替换咱们的string呢。啊,因为呢,用string呢,效率比较低是吧。哎,使用string。这个效率低,这个效率低的话呢,呃,一方面呢,就是你得。不断的去创建新的这个内存是吧。使用新的内存,然后呢,把这个数据呢,得该复制的复制过来,然后再改,另外呢,就是它空间呢,其实利用率也比较低是吧,总是得利用新的空间了,这是一个事儿。啊,然后呢,关于这哥俩的话,我们该怎么去选择呢。对说如果呢,这个开发中啊,这个不涉及到。对线程安全问题。啊,那么建议呢,是不是使用这个string builder呢来替换B。
23:08
这呢有这样的一个点啊,OK啊,因为呢,就是你线程不考虑线程问题的话呢,就不用每次调方法呢去握那个所谓的锁了,所以呢,使用它的话呢,效率要高一点是吧。啊,所以因为呢,这个使用它呢,效率高。诶OK啊行,然后还有没有再接着的这个启示啊,通我们看源码的话呢,其实还有一个启示,启示什么呢?大家你看啊,我呢,是通过这样的一个构造器,或者这样一个构造器,其实本质上来讲呢,都空了16个。假设呢,现在我们预知啊,大概能有50个字符需要去添加过来。其实呢,你要是默认的用这个构造器的话呢,先是16个,16个呢会扩成。34是吧。因为16乘以二加个二嘛。哎,就扩充34,这时候呢,得复制一次34呢,显然也不够吧。
24:00
是不是还得再扩容一次是吧,再扩容的应该就够了。对就能成效了啊,但是这块呢,我们出现过这个扩容,那能不能说我们一上来就指定这个数组空出来50个,或者你再多给俩55个。这样的话呢,是不是就避免它就扩容了。对的,哎,所以此时呢,我们可以呢,诶,在使用另外的这个构造器,CTRL f12一下,它会有这样的构造器。这个构造器的话呢,把capacity呢传进去修复一下,直接呢就指定你底层数组的长度了。诶这个呢,是不是就有可能就避免你不断的扩容的事儿了,是吧。诶这块呢,说诶如果开发中啊,大体确定。呃要呃操作的这个字符的一个个数啊,建议呢使用。啊,这个带这个咱们叫int类型叫可拍参数的构造器啊,因为呢可以避免。啊,底层是不是多次创建多次扩容吧,是吧。
25:03
哎,这样的操作,哎,性能更高啊。OK,行,这个呢,也是我们的一个启示。啊,这个呢,大家关注一下就可以了。好,刚才涉及到的话,就是我们看到这样的一个。诶点进来是吧,这样的一个代码。啊,没问题啊好。这个呢,就给我们的一个启示了,这就过掉了,然后下边这块呢,就涉及到string buffer string builder,哎,它里边呢,常用的一些方法,那么它里边这个方法都有哪些呢?直接我们来看一下这个课件了,就。BR这块呢,我们找到这个下边这个2.2啊,这呢就说它这个API的啊。这个API的话呢,呃,这块列了也有一些。啊,你看这个好像也不少啊,这块呢,给大家呢,说一个小技巧,咱们怎么去记这个事儿。诶,首先呢,我们先简单来测试一下先。跟string buffer或者叫builder啊相关的这块呢,一个测试。把这个方法呢,我们就拿过来了,这块的话,你看这个方法呢,也挺多。
26:03
包括那下边呢,也还有一波。先整体上来看一看。第一个类方法呢,叫做end。刚才的话呢,其实我们也有这个在说明的时候呢,也提到这个事儿了的话呢,它指的就是这个添加的意思了啊,诶在刚才的。这个里边是吧。嗯,在这儿这个呢,就是添加的意思了,添加的话呢,就在你现有的这个底层的差异型数组里边呢,我们去往后呢,去添加不够的话呢,你就扩容啊,就这样的一个思路了。好,嗯,这块呢,稍微注意一点,就是它返回的也是一个,这呢,我是以这个Li buffer的一个方法为例说的,它这返回的也是一个Li buffer,相当于这里边呢,会有个特点呢,我们把它称为它叫方法链的一个调用。那比如这块sli buffer呢,你就用buffer调,Builder呢就用build调,他们的主要区别呢,就是同步是是否加了一个ne的一个区别了,具体方法的话呢,其实都一样。就它ne一个string build行,我这呢,假设用那个空三的构造起来了,首先的话呢,拿着当前你这样一个变量呢,我们就调了一个pen,比如说这呢,我写了个ABC。
27:09
然后呢,它会返回一个0BUILD。就是你当前这个添加好以后的这个build了,所以你看这个open的时候呢,它其实添加完以后呢,又掉又返回了一个this this不就是当前对象吗。OK啊好,然后呢,我就可以呢,继续的在后边的去掉这个喷子了。啊,来一个123,然后你可以再继续的,我们去调这个喷的操作。啊,这个没问题是吧,EF这块呢,我们调完以后返回返回我也不接收了,我就直接呢去打印咱们这个SQ点,你说它是。一开始的时候这个空啊,还是说是ABC啊,还是说我们整个这个添加好以后的。对,就添加好以后的,因为呢,它就是在你当前的这个,呃,对象的底层呢,做调整了,你要说呢,呃,没有超出我们底层那个Y6数组的范围,那就在唯一的一个Y6数组里边呢。哎,你这块呢,去添加就行,那有可能加着加着是不是超了。
28:03
超了这个所谓的底层的速度长度16了,16话呢,那你当前这个对象里边那个Y6属性,它有没有加final限制啊,是不是你就这个重新再指定一个更长的数度就可以了。但是我们这个对象呢,还是这个对象啊,没有变化啊,所以这块呢,指向呢,就就还是它啊,它这块里边这个数据呢,就已经变了。然后呢,把这种操作啊,我们调完以后呢,接着又掉它,接着又掉它,这呢是拿着这个喷子呢,多次调用了,这种可以称为呢叫方法链的这种调用。这就一个词而已。啊就一个词而已,啊行这个了解这个事儿。呃,那么其他的这些方法的话呢,你看有这些啊,这个呢就是删了,这个呢,就是改的一些操作,这个呢,其实就查的一些操作了,诶所以这块呢,这些方法呢,比较有代表性,也算是使用比较高频的,所以呢,诶针对于string buffer或者builder长方法这块给大家做个总结,你就记住呢,他们俩里边呢,有这样的一些方法就是增。
29:01
山。改。茶。哎,还有呢,这个插入的这个方法啊,插还有一个长度啊,记这样的几个方法就可以了,增呢就是添加的意思呢,就是我们刚才看到的这个啊end。看我就光添加一个他了,这个往这儿放吧。这个呢,就是end这个操作了行山。删除是吧,删除的话呢,这块对应的有两个方法。一个呢,是这个山。一个呢,是下边这个山。啊,你看这个区别呢,能看出来吧。怎么只能改这里了?这个山的话呢,就是山好几个是吧。从这个位置开始到这个位置结束,那问包括这个结束的位置吗?对不包括这个呢,都是索引,所以呢左臂右开,然后这个呢,Delete叉呢,是不是只删一个叉。对的啊,只删这一个啊,所以这就是删的操作改修改。
30:00
修改的话呢,那这块呢,也有两个。这个呢叫replace,从这个位置开始,到这个位置结束,左臂右开,这样一个范围内的可能是多个字符了,改成是一个新的字符串。这个呢,相当于是改的是连续的多个,然后呢,你也可以呢,就改一个。这个呢叫set char,把这个位置的这个呃字符改成这个新的。那这是它了,行,然后下边这个呢,我们叫查查询,那就是指定位置的这个字符,所以呢就是查,这跟咱们string呢是一样的。哎,插入这个插入呢,用的这个叫insert。啊,在指定的这个位置呢,我们插入一个新的。啊,新的这样一个结构好,然后呢,最后这个长度呢,跟原来一样,还是叫这个length。然后呢,这块还多了一个这块写了一个叫res,就在我们这个string buffer或者string build里边呢,你想针对当前这个字串呢,想反转一下,直接调个S就行,注意string里边没这个方法。
31:01
啊,这个注意一下行,这呢,就我们说的这样几个方法,那这几个方法呢,大家看哪个不太熟,我们需要测一下的呢。其实都还行是吧。插入操作行。这个呢,比如我们先造一个这个string build。那就以它为例了,那不妨呢,在这里边我们就写上几个字符了啊,我就写这个hello吧。然后呢,拿着当前这个对象。哎,当前这个s build啊,这个对象第二啊叫insert的,这insert的时候呢,你看我们可以插入的类型呢,是比较多样的,所以这块你看我写的是叉叉啊,就是这个类型呢,这个不是单一的哈,嗯,随便,其实这块无所谓哈,就关键看你怎么写了。比如说呢,我想在这个he后边。啊,L的前面呢,插入,那这个呢应该写。二。就是012啊,我想在这个位置呢,想插入这个数据了,这个插入的类型呢,也比较多样,比如我想插入一个一。那就成这个意思了,来,我们去打印一下这个Q。
32:02
好,这个我们做一个。那这个看就成这样了。那这个呢,你相应的它对应的这个方法里边呢,你看我是不是还可以接着啊s build。第二,Insert,这个我们还写一个二吧。插入一个ABC。哎,也支持呢,我们写一个字串。啊,在这个现有的这个基础上呢,还写的是个二,那就是在这个一前面了。插入了一个ABC。那就省亮。啊没问题啊好,这个呢叫插入操作,然后这个删也好改也好,这块是不是还行。行,我这就不去测了啊。嗯,L呢,这个也不用多说了,这个有一个刚才说的一个reverse,稍微看一下。嗯,他呢,去调一个revers的这样一个方法,哎,这个方法调完以后的话呢,我们看到这个reverse呢。它的这个返回值呢,还是这个build也是当前这个Z,那问一下就是我们这个s build本身反转了吗。
33:04
哈哈。你看我们这块呢,假设哈,你拿一个值去接收。我叫s builder。S点一。我叫他吧。是吧,其实你看源码已经完全都能看出来了啊,我要去打印的话呢,我把这两个都打一下。都反转了吗?都反转了,这两个是一个吗。哎,你这看的很清楚,这不这死吗?嗯,来转一下。嗯,OK啊,首先呢,你看都反转了,然后还是个处啊,因为返回的就是当前这个对象了是吧,所以这个他俩的地址呢是一样的。哎,通过这儿呢,你看我们操作呢,大家也能发现啊,我们在通过string buffer也好,String build也好,去调相关的方法的时候呢,比如说这种插入啊,还有我们刚才呢,没有测的一些这个修改啊,删除啊,增加呀,你发现呢,它会对当前你这个。
34:03
啊,Buffer或者build的对象本身是不是就做了修改了。对返回值你要是能接收,接收完以后,你发现他跟他还是一样的啊,还都是这种这次的这种场景。哎,它不像咱们说的string string呢叫不可变的,就是你调完一个方法,比如我们想做一个replace操作,那你返回的值是你改以后的string,这个本身不变。所以叫不可变的字符序列。啊,体会一下这个设计的一个原理啊。好,那么这呢,我们关于这个方法这个呢,我们就啊这还没完,这块还有一波呢,是吧,这一波的话呢,大家作为一个。了解熟悉就行,比如说你像这inex of nothing inex of subst string to string这块呢,我们讲string的时候呢,是不是也有。那这个呢,是咱们这个string buffer string里边的A啊,它也有一些类似于string当中的一些方法。啊,这个了解一下就行,哎,有一个这个啊。这个的话呢,有的时候呢,在一些特殊场景下呢,我们可能会用到了。
35:01
啊,这个呢叫line。咱们上面有个方法呢,叫lance。就是返回你这个底层的,呃,相当于什么呀,存储的实打实的这个数据,它有几个长度。没问题吧?哎,这块呢,还有必要,这块我们得说一下啊,赶紧看。我现在的话呢,我调一下这个string build,它的一个length方法,我去打印一下,大家注意啊,这时候你说打出来的是底层。嗯,咱们是在。啊,咱们这个长度已经变了是吧,你说这块打的是我们这个数组的长度16呢,还是说呢,我们现在存的这样的几个字符,它的长度呢。啊,注意这块呢,对,返回的就是你实打实的存了几个,所以我们去调这个lengths的时候呢,你看它返回的就是count count不就是记录你到底存了几个吗。嗯,所以你看我们这块去做一个run。
36:00
你看这个就是九是吧,呃,因为这块不是有九个字符嘛,这个呢,大家注意它返回的就是你实际。哎,存储的字符的个数。这个没问题是吧,好,那这个要清楚以后的话呢,那么接着呢,大家你再看。啊,这个我叫三了。咱们刚才有个方法呢,叫。其实就相当于呢,对这个length呢,就相当于这个呢,类似于是个get方法了,那side length就相当于是对应的set方法了。行,比如说呢,我现在呢,是还是这个叫hello是吧。然后现在呢,我拿着它呢去调叫set Les,把它呢改成比如说是二吧。那这时候我们就要问,到底这个改是改的什么是吧?啊,这时候我们先去打印一下这个,咱们看看它变成什么了。大家看。它就变成了叫he了。哎,相当于呢,这个LAS改的啥呢,咱们走一下啊,说你这块如果是零抛异常了,这个这都不重要啊,主要你就看这。
37:08
最核心的代码。他把这个can呢,改成是这个你设置的这个length了。啊,这个呢,就我们看到这样场景,也就是说啊,回过来我们现在这个底层的话呢,它其实这个value呢,是16个长度的,现在呢,存着h he lo,然后这个count呢,其实这时候呢是五是吧。五个元素,然后呢,你这块S呢,这个我写的是二,它其实呢,就这时候只取到这个位置了,相当于。就相当于我现在认为呢,就he,其实后边这几个数呢没动。还放在那儿呢。当然了,就是你在打印的时候呢,我们其实只取你有几个,它只考虑count,所以这块呢,只打印了它了。所以说我们要现在去再调end的操作。是怎么着啊?是从这开始起这个喷的,还是从这开始啊?前边还是后边?
38:00
后边。前面儿。因为呢,这时候count已经实打实的就变成二了。他呢,就是记录说我们有几个,他就认为有俩,所以你再加的话呢,是不是应该从这个位置接着往后加了。对,说白了就是这个数据呢,即使没去,你虽然放在这儿,但是我认为已经没有了,我只是没有给你清掉而已。我们各种操作呢,其实已经不考虑它了。嗯,也就是说呢,你看在这个基础之上,我们通过它呢,点我去调这个open的操作,咱们简单一点,比如我就整一个C吧。是吧,这时候其实是把这个C呢,就把这个L给覆盖了。啊,你再打印的话呢,那也不会出现你后边那个L和O的。那就多了一个C而已。那就这样啊,行。这就我们说的叫,那可能有同学会说,那现在这个长度,你看是不是五啊。那如果说我们要是s build,我点我整成是一个十呢。
39:01
那我们现在呢,到这个为止为止,咱们其实存的呢,现在是。Ec是吧,然后现在改成十了。我打印一下。S点是吧,我们再去做一个run。他这块的话,你发现呢,它都整成空了,你说哎这个L和O不是还有吗。对这块呢,它其实呢,就set的时候呢,哎,你看啊,他发现呢,你这个这个是小于零,这是一个场景,它这个位置呢,这不还有这个count跟他去做比较吗。这个比较的时候呢,咱们刚才这个抗呢,是不是已经是三了。然后这个呢,咱们写了个十,在小的时候呢,他其实就从你这个后边开始呢,就拿这个新的去补了。就是相当于你后边即使有这个L和O呢,它是不是也直接就给你覆盖掉了是吧。那这个覆盖的这个是谁呢?这个其实就是咱们说的那个默认值那个零。对,就是你看这个杠U0000,其实就是啊,只不过我们在这打印出来,他看到不就是这样的效果吗。
40:03
就就这个事儿,你看啊,我要是拿这个s build点我们就查嘛。这个咱们选一个,我选一个,选一个六吧,小六是吧,它呢等等于。哎,不能加。这个这个单引号了哈,是不是跟他呀。这个呢,我要判断一下是不是true,这个是true false。对,这就是处,就说白了就是你现在要设置这个超出了我本身这里边存的这几个字符的个数了,那我就拿零给你先填充一下。就这意思。那这不是个出嘛,是吧。就行啊行,这个大家了解一下。啊就可以了,好,那这呢,我们把这个常用的方法呢,讲了挺多的,但是这块呢,大家主要关注就是增删改,查查长度。哎,把这些方法呢,你记住,或者你记不住的话,你知道呢,他们里边有这些方法,回头你再找API呢也行。下边呢,多一个操作叫对比三者的执行效率,就我们提到了string string buffer string builder,哎,他们三者的这个效率,其实我们要不对比的话,大家其实也知道这个效率吧。
41:08
这个效率要是从高到低。凯丽。应该是西安。对吧。哎,然后呢的。八然后呢是string是吧,诶注意这样的这个呢,效率指的什么呢?就是我们现在针对字符串呢,做一些操作的时候,尤其是增删改的时候呢,他们的一个效率的一个排序。OK,好,那这个呢,咱们是可以测试的,我在这下边儿呢,写了一个对应的测试的一个代码,咱把这个代码呢,就直接拿过来,因为它比较简单啊,直接咱们看一看就行。达到这个测试。这个呢是四。诶,把那代码粘过来。在这儿我们写一下。哎,测试这个string。String buff。哎,这个。
42:02
哎,在这个操作数据方面的效率。好,那这个呢,是什么样的思路,这块我定义两个变量来记录这个起起始和结束的这个时间的这块是初始的,先把这三个变量呢先定义出来,我呢从零开始,一直到这个两千两万的这个前一个数啊,19999,我们添加进来。然后呢,看一下这个结束的时间,看他花了多少时间,然后呢,我再演示一下这个string builder。也是从零开始一直到啊,19999,把它呢,添加到我们这个build里边,看看花了多少时间,然后最后呢,我这块演示的是string。此阵呢,就得通过拼接的方式来做了,那很显然这个效率呢比较低,底层呢,是不是不断的去逆新的字串是吧。OK,看这呢,花了多少时间?好,我们做一个run。哎,看出来了。啊,然后呢,你看。
43:00
这个string的话呢,跟他们比呢,是数量级呢,你看都差出来两个数量级了,然后他俩呢,差别呢不是特别大,这二跟四你说这是差一倍,别这样算了。你就说差两毫秒是吧。啊,这个就别按倍数来说了,因为这个数本身就很小啊,它俩呢,主要区别就是这个string builder呢,它呢不需要去握锁了,而这个呢,它得有一个锁的一个处理是吧。诶,所以它稍微的会慢一点啊,插到这儿了啊,这儿呢,主要是因为你不断的去新建这块呢,它的时间会很多,所以呢,大家也知道了,如果频繁的对字符串做操作的话呢,建议呢,你使用前边的不要用这个string去操作。好,这呢我们就过掉了,接下来的话呢,我们来看一看这个对应的有几个相应的这个笔试题。看看你会不会做啊,这里边儿呢,有一些还是涉及到值传递的问题了。先看第一道题。String buffer string buffer一个A一个B,然后呢,传递到我们这个方法的两个参数的位置。判断这个值是多少。
44:01
看一看啊。哎,你就始终坚持咱们说那个原则,零类型传递值X呢也指向它。Y呢,指向这个。然后呢,X基础上呢,判了一下这个Y。X跟着变了,其实我们整个的A呢,是不是也跟着给处理变了,所以他在打A的时候呢,你顺带这块呢,稍微可以写一下啊,目前呢,相当于是里边呢,是不是加了个B啊。暂时这个是吧,然后呢,把这个X呢给到Y了。相当于这个Y呢,目前指的其实也是AB了。然后这个Y的话呢。啊,判断一下X。这个会跟着变吗?会不会?那不会,你这个Y呢,是不是指的是个地址,这个地址呢,指向的也是你对空间实打实的那个对象了。所以呢,会变吗?所以这就变成X了。
45:00
你看A的地址给了X了。X呢?又给了Y了,Y不就也指向它吗?所以这块你再添加是不是也会跟着。变呗,是吧。哎,所以呢,出来以后的话呢,我们这个A呢,是不是就变成abx了,然后这块有个逗号嘛,然后B呢。B呢,这块地址给了Y了,那Y呢,把地址给改了,他操作完以后呢,实际上这个没改变是吧,是不是它还是个B啊。所以这个结果呢,就是这样的了。哎,乱一下。你看这不就这样吗?稍微体会一下。然后看第二个。这个稍微有点长,咱们这样看。嗯,这个先从main方法来看吧,这这呢是一个string。这是一个string buffer。好,然后呢,这个string呢这块哎放进去string b呢放进去,这就算是两个独立的操作了,先看string。
46:00
嗯,放到这个位置。然后这个地址过来,他呢做了一个。把勾,把勾呢换成I。呃,替换完以后呢,又给了他。然后呢,这块打印一下这个。走串这个是啥?是Java还是?吧。Java是吧,这个呢,不就体现不可变形吗?诶虽然我把地址给你了,然后你里边替换了,但是我们这个本身呢,不要动,你先去创建一个,然后呢,诶给你这又重新复制了,诶改的是你这个行参的这个组串啊,本身我这个呢是没有变的,所以它还是Java。好,然后呢,它是扎完了,然后接着看下边这个,这是string buffer。把它呢传进去,这个地址呢,就给了他了。他呢,喷了一个C。这目前呢,就先变成是RC了,对吧。然后的话呢,他又test重新又扭了一个。
47:01
添加了一个word。那么这个操作呢,会不会影响这个。对这个不会了,因为呢,我是把我的地址给了你了,你这块操作呢,还是影响我的,但是接下来你又重新又附了一个地址。跟我就没关系了,对吧。所以呢,诶我们这个就是Java c。软一下。诶,看没问题。啊,体会一下啊。好,第三个题。第三个题的话呢,这块你看就有这个sidelines了。咱们刚才也说过这个操作它是什么意思了。还没方法。S呢,复制为四个B。啊了一个string buffer也是四个。把他俩放进去,放进去,放进去好打印。好,那先看这个前面这个啊,这个S呢,是拿你这个BBBB整过来的,然后改成AA了。
48:00
那不好意思,我这个是不是,我这个S是不是不动啊。所以这块他还是。这个币是吧。下边这块,因为你都是操作这个buffer了啊。嗯,这个不动,然后接着斯林巴呢,是四个B。把地址给过去,地址给过去一个是。哇,他竟然写成零了。现在这个count就成零了是吧。事实上的话,就是这块基本上都可以忽略,没有这个数了吧。然后呢,他呃喷了一下四个A。这个呢,是拿着你现有的传过来的这个地址改的,你实打实的这个对象的length和openend,所以呢,是不是对它进进行了修改啊。所以这块呢,就改成了。A,是吧?哎,来,再去run一下。啊,确实如此啊,所以你看这个笔试题啊,有的时候他还。你得小心一点处理啊,好多这个还是挺阴险的啊。好,这三道呢,如果你觉得还行,这道题的话呢,就比较难了。
49:04
啊,这个呢,有三个位置,谁要谁做对了,中午请他吃饭。全做对了,就。先看第一个。啊。这是一个string。八。啊,喷了一下。平时没正常写过是吧?所以笔试的时候才才会出现这么恶心的题哈问。Nice。第一个就卡住了是吧。这个是多少呢?其实无外乎就几种情况吧。另一种。是一种。还有什么?劫。十六一种啊啊行,16也写上了,16其实有点过分是吧?嗯,还有呢,就是说加的时候会不会直接在里边添加的时候就直接报空指针异常了呢。
50:08
啊,不会吗?添加了一个不存在的东西。不会啊,其实这个题倒是不会是吧,但是你可以呢,从一种错误的角度来讲说,有可能有这样的担心是吧。啊,那么。哪个队呀?这个概率是不是就挺高的啊,行,那先放这儿啊好,这个呢,如果你还没整明白的时候,然后我们再看下边这个打印一下,这这是啥。你要说是零的话呢。是空是吧。啊。行L4呢。闹闹的话,你这个闹是不是得是这个闹啊。是吧,哎,那有没有可能他打出来还是个这个闹呢。没想过16呢,这个就咱就退出这个。争夺得了是吧。
51:00
是不是有这样好几种情况是吧?然后呢,再接着往后看。他这呢,诶还是一个string buffer,跟这个string buff没关系了,他把这个HT2呢,作为一个参数传过来的。问,这个时候他是什么样子的?这个相当于是有判断的方式,这个呢,相当于是呃,传参的方式。这个呢?呃,感觉跟这个一样是吧。是这样吗?嗯。就这样几种情况呗。这里边儿咱们看看有对的不。跑一下吧。走一下。哎哟,好像还有点儿问题。是吧,来看第一个啊,第一个是四。就64了。哎,第二是闹这个闹呢。对是四嘛,所以呢,肯定就是这个了。
52:01
诶就这个了,咱先看这个事儿啊,为什么是这样子的,其实这个呢,都来源于源码了它啊end时候呢,一进来这个是个now。这是个闹点进来这个闹说,你要是闹,我就这么着。这干啥呢,点进来。他给你把当成NUL4个字符给你存进去了。啊,这个抗这不加加过四次,这个就是四嘛,所以存了四个字符。啊是这样的方式的,所以这块呢,我们这个长就是四,它这块呢,字符串就看成是nul这个字符串了。这个注意,那下边这个呢,报错了啊,控制异常。控制异常了,这是因为呢,我们通过这种方式来添加的时候呢,你这个是一个no no.length的时候呢,就控制人了。啊,防不胜防是吧。哎,控制帧啊异常。所以这个饭呢,不太好请啊。这个呢很难做。啊,这个呢,其实列出了它的意义上呢,就是大家在写的时候呢,其实呃,这个有很多位置啊,其实你要考虑这种闹的这种场景,包括呢,我们在写算法题的时候呢,有的时候呢,可能也会忽略这个事儿,就是我们在调eo的方法呀,是吧,调con呀,实际上都有可能它的调用者呢,是一个nor是吧。
53:16
诶,你要让这个程序的兼容性好一点呢,可以提前呢去加一个判断这样的逻辑OK。好,这个呢,就是我们STRING8STRING build呢的这样的几道题目,那整个呢,关于string相关的,诶咱们就告一段落了。
我来说两句