StringBuffer和String区别

StringStringBufferStringBuilder的区别

java.lang.StringBuffer线程安全的可变字符序列。一个类似于String的字符串缓冲区,但不能修改。虽然在任意时间点上它都包含某种特定的字符序列,但通过某些方法可以改变该序列的长度和内容。可将字符串缓冲区安全地用于多个线程。可以在必要时对这些方法进行同步,因此任意特定实例上的所有操作就好像是以串行顺序发生的,该顺序与所涉及的每个线程进行的方法调用顺序一致。

StringBuffer上的主要操作是appendinsert,可重载这些方法,以接受任意类型的数据。每个方法都能有效地将给定的数据转换成字符串,然后将该字符串的字符追加或插入到字符串缓冲区中。append 方法始终将这些字符添加到缓冲区的末端;而 insert 方法则在指定的点添加字符。例如,如果 z 引用一个当前内容是start的字符串缓冲区对象,则此方法调用z.append("le")会使字符串缓冲区包含startle,而 z.insert(4, "le")将更改字符串缓冲区,使之包含starlet。通常,如果sb引用StringBuffer的一个实例,则 sb.append(x) sb.insert(sb.length(), x) 具有相同的效果。

只要发生有关源序列的操作,比如,在源序列中追加或插入,该类就只在字符串缓冲区上执行此操作而不是在源上实现同步。

每个字符串缓冲区都有一定的容量,只要字符串缓冲区所包含的字符序列的长度没有超出此容量,就无需分配新的内部缓冲区数组。如果内部缓冲区溢出,则此容量自动增大。从 JDK5开始,为该类补充了一个单线程使用的等价类,即 StringBuilder。与该类相比,通常应该优先使用StringBuilder类,因为它支持所有相同的操作,但由于它不执行同步,所以速度更快。

java.lang.StringBuilder一个可变的字符序列。此类提供一个与 StringBuffer 兼容的 API,但不保证同步。该类被设计用作 StringBuffer 的一个简易替换,一般用在字符串缓冲区被单线程使用的时候。如果可能建议优先采用该类,因为在大多数实现中,它比StringBuffer要快。

StringBuilder上的主要操作是append insert,可重载这些方法,以接受任意类型的数据。每个方法都能有效地将给定的数据转换成字符串,然后将该字符串的字符追加或插入到字符串生成器中。append方法始终将这些字符添加到生成器的末端;而insert方法则在指定的点添加字符。例如,如果 z 引用一个当前内容为start的字符串生成器对象,则该方法调用 z.append("le") 将使字符串生成器包含startle,而 z.insert(4, "le") 将更改字符串生成器,使之包含starlet。通常,如果 sb 引用 StringBuilder 的一个实例,则sb.append(x) sb.insert(sb.length(), x) 具有相同的效果。

每个字符串生成器都有一定的容量,只要字符串生成器所包含的字符序列的长度没有超出此容量,就无需分配新的内部缓冲区。如果内部缓冲区溢出,则此容量自动增大。

StringBuilder的实例用于多个线程是不安全的。如果需要这样的同步,则建议使用StringBuffer

String的创建

   String s = "hello";

   JVM先根据内容"hello"查找对象,如果没有找到,则在heap上创建新对象,并将其赋予s,否则使用已经存在的对象。

   String s = new String("hello");

   JVM直接在heap上创建新的对象,所以在heap中会出现内容相同,地址不同的String对象。

"hello"就是一个对象,因此才可以String s1="hello";才可以String s2=new String("hello");

String的比较:

"=="         比较地址

"equals"    比较内容

==是判断两个变量或实例是不是指向同一个内存空间,即引用。

equals是判断两个变量或实例所指向的内存空间的值是不是相同。

举例:

   String s1 = "hello";

   String s2 = "hello";

   String s3 = new String("hello");

   s1 == s2;                 // true         地址相同

   s1 == s3;                 // false        地址不同

   s1.equals(s2);           // true         内容相同

   s1.equals(s3);           // true         内容相同

intern()方法,查找内容相同(equals())的字符串

     String s1 = "hello";                 // hello不存在,jvm创建新对象 (1)

     String s2 = new String("hello");    //创建新对象 (2),这时heap中存在两个内容为hello的对象

     s1 == s2;             // false        地址不同

     s1.equals(s2);       // true         内容相同

     s2 = s1.intern();   //               找到对象(1)并赋予s2

     s1 == s2;             // true         注意:此时s1,s2同指向(1)

效率:String StringBuffer

     情景1

     (1)String result = "hello" + " world";

     (2)StringBuffer result = new String().append("hello").append(" world");

     (1)的效率好于(2),不要奇怪,这是因为JVM会做如下处理

         编译前String result = "hello" + " world";

         编译后String result = "hello world";

     情景2

     (1) public String getString(String s1, String s2) {

             return s1 + s2;

         }

     (2) public String getString(String s1, String s2) {

             return new StringBuffer().append(s1).append(s2);

         }

     (1)的效率与(2)一样,这是因为JVM会做如下处理

         编译前return s1 + s2;

         编译后return new StringBuffer().append(s1).append(s2);

     情景3

     (1)String s = "s1";

               s += "s2";

               s += "s3";

     (2)StringBuffer s = new StringBuffer().append("s1").append("s2").append("s3");

     (2)的效率好于(1),因为String是不可变对象,每次"+="操作都会造成构造新的String对象

     情景4

     (1)StringBuffer s = new StringBuffer();

         for (int i = 0; i < 50000; i ++) {

             s.append("hello");

         }

     (2)StringBuffer s = new StringBuffer(250000);

         for (int i = 0; i < 50000; i ++) {

             s.append("hello");

         }

     (2)的效率好于(1),因为StringBuffer内部实现是char数组,默认初始化长度为16,每当字符串长度大于char数组长度的时候,JVM会构造更大的新数组,并将原先的数组内容复制到新数组,(2)避免了复制数组的开销

关键点

     1)简单的认为append()效率好于"+"是错误的

     2)不要使用new创建String

     3)注意intern()的使用

     4)在编译期能够确定字符串值的情况下,使用"+"效率最高

     5)避免使用"+="来构造字符串

     6)在声明StringBuffer对象的时候,指定合适的容量,不要使用默认值(16)

     7)注意以下二者的区别不一样

       String s = "a" + "b";

       String s = "a";

                s += "b";

我觉得说String的字符串不能改变话是不错,但是例子要举好

看看下面这个简单的例子:

首先,

public class xx {

public static void main(String[] args) {

String s1 = "You are hired!";

String s2 = "You are hired!";

if (s1==s2) {

System.out.println("一个内存空间");

} else {

System.out.println("不是一个内存空间");

}

}

}

打印的结果是:一个内存空间

这里==的意义是两个引用是否指向同一个对象

可见s2在不用new创建的情况下会自动检索到具有相同内容的内存空间中共享,那么既然s1s2共享了同一个对象

再看下面的代码

public class xx {

public static void main(String[] args) {

String s1 = "You are hired!";

String s2 = "You are hired!";

s1 = s1.replace('h','f');

System.out.println(s1);

if (s1==s2) {

System.out.println("一个内存空间");

} else {

System.out.println("不是一个内存空间");

}

}

}

代码结果是:You are fired!不是一个内存空间

可见,Strings1的内容虽然被改写,但是已经不在是原来第一次分配到的那个内存空间,也就是String类的内容能被改变,但一旦改变系统将为其分配新的内存空间。

说到与stringBuffer的区别,从根本上来说,stringBuffer在做字符长度变动的时候将继续使用原来的内存空间,不重新分配内存空间。而String的长度一旦变动,就如上面的例子一样,其内部将分配新的内存空间。

java中有3个类来负责字符的操作。

1.Character是进行单个字符操作的。

2.String对一串字符进行操作,不可变类。

3.StringBuffer也是对一串字符进行操作,但是可变类。

String:是对象不是原始类型,为不可变对象,一旦被创建,就不能修改它的值。对于已经存在的String对象的修改都是重新创建一个新的对象,然后把新的值保存进去。

String final,即不能被继承。

StringBuffer:是一个可变对象,当对其进行修改的时候不会像String那样重新建立对象,它只能通过构造函数来建立:StringBuffer sb = new StringBuffer();不能通过赋值符号对他进行赋值:StringBuffer sb = "welcome to here!";//error

对象被建立以后,在内存中就会分配内存空间,并初始保存一个null,向StringBuffer

中赋值的时候可以通过它的append方法:sb.append("hello");

字符串连接操作中StringBuffer的效率要比String:

String str = new String("welcome to ");

str += "here";

的处理步骤实际上是通过建立一个StringBuffer,然后调用append(),最后再将StringBuffer toSting();这样的话String的连接操作就比StringBuffer多出了一些附加操作,当然效率上要打折扣。并且由于String是不可变对象,每次操作String 都会重新建立新的对象来保存新的值。这样原来的对象就没用了,就要被垃圾回收。这也是要影响性能的。

看看以下代码:

26个英文字母重复加了5000次,

String tempstr = "abcdefghijklmnopqrstuvwxyz";

int times = 5000;

long lstart1=System.currentTimeMillis();

String str ="";

for(int i=0;i<times;i++){

str+=tempstr;

}

long lend1=System.currentTimeMillis();

long time1 = lend1-lstart1;

System.out.println(time1);

可惜我的计算机不是超级计算机,得到的结果每次不一定一样一般为 154735 左右。也就是154秒。

我们再看看以下代码:

String tempstr = "abcdefghijklmnopqrstuvwxyz";

int times = 5000;

long lstart2=System.currentTimeMillis();

StringBuffer sb =new StringBuffer();

for(int i=0;i<times;i++){

sb.append(tempstr);

}

long lend2=System.currentTimeMillis();

long time2 = lend2-lstart2;

System.out.println(time2);

得到的结果为 16 有时还是 0

所以结论很明显,StringBuffer的速度几乎是String上万倍。当然这个数据不是很准确。因为循环的次数在100000次的时候,差异更大。不信你试试。

总结: 如果在程序中需要对字符串进行频繁的修改连接操作的话,使用StringBuffer性能会更高。

 

 

StringStringBuffer的区别

StringStringBuffer的主要性能区别其实在于 String 是不可变的对象,因此在每次对String类型进行改变的时候其实都等同于生成了一个新的 String 对象,然后将引用指向新的String对象,所以经常改变内容的字符串最好不要用 String ,因为每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了以后,JVM GC就会开始工作,那速度是一定会相当慢的。

而如果是使用 StringBuffer 类则结果就不一样了,每次结果都会对 StringBuffer 对象本身进行操作,而不是生成新的对象,不改变对象引用。所以在一般情况下我们推荐使用 StringBuffer,特别是字符串对象经常改变的情况下。而在某些特别情况下, String 对象的字符串拼接其实是被JVM解释成了StringBuffer对象的拼接,所以这些时候 String对象的速度并不会比StringBuffer对象慢,而特别是以下的字符串对象生成中, String效率是远要比StringBuffer快的:

String s1 = "This is only a" + " simple" + " test";

StringBuffer sb = new StringBuffer("This is only a").append(" simple").append(" test");

你会很惊讶的发现,生成String s1对象的速度简直太快了,而这个时候 StringBuffer 居然速度上根本一点都不占优势。其实这是 JVM 的一个把戏,在 JVM 眼里,这个String s1 = "This is only a" + " simple" + "test"; 其实就是:String s1 = "This is only a simple test"; 所以当然不需要太多的时间了。但大家这里要注意的是,如果你的字符串是来自另外的 String 对象的话,速度就没那么快了,譬如:

String s2 = "This is only a";

String s3 = " simple";

String s4 = " test";

String s1 = s2 + s3 + s4;

这时候 JVM 会规规矩矩的按照原来的方式去做, s1对象的生成速度就不像刚才那么快了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值