说说String和StringBuffer的那些事

由于我们公司对于性能要求越来越高,所以需要员工关注每一行代码的效率,每一行代码可以节约1毫秒,那么积累起来

将是不可估量的,只有注重每一个细节才可以真正的提高产品品质。

细节决定成败,是一个经典语句。

说道String和StringBuffer的性能,java工程师很容易的认为StringBuffer的效率肯定优于String,但是你真的深入研究过区别吗?

请看下面的例子:

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

(1)的效率要高于(2)的效率,尝试代码

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

      String result = "hello" + " world";

}

--------------------------------------------

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

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

}

上面两个代码运行消耗可以相差几十毫秒,甚至100多毫秒。

有人问原因。原因就是:String对象在编译期就决定了而StringBuffer对象是在运行期决定的。

运行期决定需要额外的开销当字符串的值无法预先知道的时候,编译期决定作用于字符串的值可以预先知道的时候,下面是一个例子。

编译前:

public String getString(String str1,String str2) {
    String result = "This is"+ "testing the"+ "difference"+ "between"+
            "String"+ "and"+ "StringBuffer";
}
public String getString(String str1,String str2) {
   StringBuffer result = new StringBuffer();
        result.append("This is");
        result.append("testing the");
        result.append("difference");
        result.append("between");
       result.append("String");
       result.append("and");
       result.append("StringBuffer");
}


上面的程序第二种消耗的时间就比第一种少,原因就是运行期决定需要更多的时间来运行。


下面程序请看一下:

(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)避免了复制数组的开销。

StringBuffer的缺省行为:
  StringBuffer在内部维护一个字符数组,当你使用缺省的构造函数来创建StringBuffer对象的时候,

因为没有设置初始化字符长度,StringBuffer的容量被初始化为16个字符,也就是说缺省容量就是16

个字符。当StringBuffer达到最大容量的时候,它会将自身容量增加到当前的2倍再加2,也就是(2*旧值+2)。

如果你使用缺省值,初始化之后接着往里面追加字符,在你追加到第16个字符的时候它会将容量增加

到34(2*16+2),当追加到34个字符的时候就会将容量增加到70(2*34+2)。无论何时只要

StringBuffer到达它的最大容量它就不得不创建一个新的字符数组然后重新将旧字符和新字符都拷贝一遍

――这也太昂贵了点。

所以总是给StringBuffer设置一个合理的初始化容量值是错不了的,这样会带来立竿见影的性能增益。

合适的容量值来初始化StringBuffer永远都是一个最佳的建议,合理的使用StringBuffer对于系统资源也是一个很好的优化。


以上问题大家明白了吗?那么看如下例子:

(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);
  }


是不是就会以为 (2)优于 (1)啊,:-)恭喜你有错啦。

他俩的效率是一样的,原因:

vm会做如下处理
  编译前  return s1 + s2;
  编译后  return new StringBuffer().append(s1).append(s2);


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

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

    创建多对象也就变相的消耗系统资源。


下面的程序结果那个时间长呢,自己试一试吧,往往你理所当然的事情并不是正确的。


       int T = 190;
        long startTime = System.currentTimeMillis();
        
        for(int i=0;i< 500000;i ++){
            String s = T+"";
        }
        long endTime = System.currentTimeMillis();
        
        System.out.println("T+  mode :endTime-startTime="+(endTime-startTime));
        
        startTime = System.currentTimeMillis();
        for(int i=0;i< 500000;i ++){
            String s =String.valueOf(T);
        }
        endTime = System.currentTimeMillis();
        
        System.out.println("String.valueOf mode  :endTime-startTime"+(endTime-startTime));
        
        
        startTime = System.currentTimeMillis();
        for(int i=0;i< 500000;i ++){
            String s =Integer.toString(T);
        }
        endTime = System.currentTimeMillis();
        
        System.out.println("Integer.toString mode :endTime-startTime"+(endTime-startTime));


关键点
  1). 简单的认为 .append() 效率好于 "+" 是错误的!
  2). 不要使用 new 创建 String
  3). 注意 .intern() 的使用
  4). 在编译期能够确定字符串值的情况下,使用"+"效率最高
  5). 避免使用 "+=" 来构造字符串
  6). 在声明StringBuffer对象的时候,指定合适的capacity,不要使用默认值(18)






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值