今天来讲讲StringBuffer和StringBuilder这两个类的区别
说这两个类之前,先来简单提一提String这个类,String的值是不可变的,每次对String的操作都会生成一个新的String对象,不仅效率低,而且占用大量的内存空间。
那么有没有一种办法可以操作字符串并且效率不挫,还不占用大量的内存空间呢?
答案是有,这就得说说StringBuffer和StringBuider这两个类了。
先来说说StringBuffer类
StringBuffer类和String类一样,也用来表示字符串,但是StringBuffer的内部实现方式和String类不同,在进行字符串处理时,不生成新的对象,就在内存上也要优于String。
StringBuffer默认分配16字节长度的缓冲区,当字符串超过大小时,会自动增加缓冲区长度,而不是生成新的对象。
StringBuffer不像String,没有简写,只能通过new关键字来创建一个StringBuffer对象,例如:
StringBuffer sb1=new StringBuffer();//默认分配6字节长度的缓冲区 StringBuffer sb2=new StringBuffer(32);//分配32字节长度的缓冲区 StringBuffer sb3=new StringBuffer("Hello World");//在缓冲区中存放了字符串,并在字符串后面预留了16个字节长度的缓冲区
StringBuffer类的主要常用方法
StringBuffer类中的方法主要偏向对字符串的操作,例如追加、插入、替换和删除等。实际开发中,如果要对一个字符串进行频繁的修改,建议使用StringBuffer。
- append(String str)
把str字符串追加到此StringBuffer对象后
StringBuffer sb=new StringBuffer("Hello"); sb.append("World");
此时追加后,对象sb的值将变成“HelloWorld”,注意时sb指向的内容变了,而不是sb的指向变了。
字符串的“+”操作实际上也是先创建一个StringBuffer对象,然后调用append()方法将字符串拼接起来,最后调用toString()方法转换成字符串。
- charAt(int index)
根据索引返回此处的char值
- delete(int beginIndex,int endIndex)
移除从beginIndex索引开始到endIndex索引结束的字符串
StringBuffer sb=new StringBuffer("HelloWorld");//此时创建对象为“HelloWorld” sb.delete(3, 5);//删除索引从3到5之前的字符(下标从0开始),结果为“HelWorld”
- deleteCharAt(int index)
移除索引index处的char值
- indexOf(String str)
返回第一次出现指定子字符串在该字符串中的索引
- insert(String str,int fromIndex)
将String类型的参数str插入到指定索引fromIndex处
- lastIndexOf(String str)
返回最后一次出现指定子字符串在该字符串中的索引
- length()
返回此字符串的长度
- replace(int start , int end , String str)
使用给定String中的字符替换此序列的子字符串中的字符
StringBuffer sb=new StringBuffer("HelloWorld");//此时创建对象为“HelloWorld” sb.replace(3, 5, "1111");//替换完之后变成了“Hel1111World”
注意:这里替换实际上等同于先把索引从3到5之前的字符移除,然后在把字符串“1111”从3开始插入,然后 在加上后面剩余的字符。
- reverse()
将此字符序列反转形式返回
StringBuffer sb=new StringBuffer("HelloWorld");//此时创建对象为“HelloWorld” System.out.println(sb.reverse().toString());//反转序列为“dlroWolleH”
- subString(int start , int end)
截取字符串
StringBuilder类
StringBuilder类和StringBuffer类功能基本相似,方法也差不多,主要区别在于StringBuffer类的方法是多线程安全的,而StringBuilder类的方法不是线程安全的,所以相比较StringBuffer,StringBuilder的执行效率能更快一些。
而StringBuffer、StringBuilder和String都实现了CharSequence接口
CharSequence是一个定义字符串操作的接口,它只包括length()、charAt(int index)、subSequence(int start,int end)这几个方法。
StringBuffer、StringBuilder、String对CharSequence接口的实现工程不一样,如下图所示:
可见,String类是直接实现了CharSequence接口,StringBuilder和StringBuffer都是可变的字符序列,它们都继承于AbstractStringBuilder类,而AbstractStringBuilder类则实现了CharSequence接口。
效率对比
//分别对String、StringBuffer、StringBuilder这三个类的字符串对象把字符‘A’加了100000次 int n=100000; char c='A'; String str=""; StringBuffer strBuffer=new StringBuffer(""); StringBuilder strBuilder=new StringBuilder(""); //String对象 long startTime1=System.currentTimeMillis();//获得当前时间,单位是long型 for(int i=1;i<n;i++){ str+=c; } long endTime1=System.currentTimeMillis(); System.out.println("String: "+(endTime1-startTime1)+" ms"); //StringBuffer对象 long startTime2=System.currentTimeMillis(); for(int i=1;i<n;i++){ strBuffer.append(c); } long endTime2=System.currentTimeMillis();//获得当前时间,单位是long型 System.out.println("StringBuffer: "+(endTime2-startTime2)+" ms"); //StringBuilder对象 long startTime3=System.currentTimeMillis(); for(int i=1;i<n;i++){ strBuilder.append(c); } long endTime3=System.currentTimeMillis();//获得当前时间,单位是long型 System.out.println("StringBuilder: "+(endTime3-startTime3)+" ms");
运行结果:
String: 5337 ms StringBuffer: 3 ms StringBuilder: 1 ms
结果很明显,StringBuffer的执行效率比String快千倍,这个差异随着叠加次数的增加越来越明显,而StringBuilder则由于不用考虑线程安全问题,效率比StringBuffer还要快。
所以,强烈建议再涉及大量对字符串操作时使用StringBuffer或者StringBuilder。
而StringBuffer类是线程安全的,而StringBuilder类不是线程安全的,读者可以依情况选择使用哪个类。
结论
线程安全:
- StringBuffer:线程安全
- StringBuilder:线程不安全
速度:
- 一般情况下,速度从快到慢为StringBuilder > StringBuffer > String,当然这是相对的,不是绝对的。
使用场景 :
当操作少量数据时使用String
不考虑线程安全问题并操作大量数据使用StringBuilder
需要考虑线程安全问题并操作大量数据使用StringBuffer