`
这些年
  • 浏览: 389650 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

字符串优化(转)

    博客分类:
  • java
 
阅读更多

本文部分内容来自于《突破程序员基本功的16课》,《Java程序性能优化》

1、拼接

1.1直接量拼接、变量拼接、fianl变量拼接


上干货,fuck goods

 

Java代码  收藏代码
  1. public class StringTest {  
  2.   
  3.     public static void main(String[] args) {  
  4.         test();  
  5.         test2();  
  6.         test3();  
  7.   
  8.     }  
  9.     /** 
  10.      * 直接量拼接,对于编译期就能确定的值,编译器会将值合并 
  11.      * String hw = "hello" + "world";反编译class 
  12.      * 我们将看到  
  13.      * String hw = "helloworld"; 
  14.      *  
  15.      * 所以hw == helloWorld 输出true 
  16.      */  
  17.     public static void test(){  
  18.         String hw = "hello" + "world";  
  19.         String helloWolrd = "helloworld";  
  20.         p(hw == helloWolrd);  
  21.     }  
  22.     /** 
  23.      * hw在编译期并不能确定值,因为h是变量,JVM在运行期才能确定其值 
  24.      * 会在运行期时,进行字串拼接生成新的字串对象,反编译class后 
  25.      * String hw = new StringBuilder(h).append("world"); 
  26.      * 
  27.      * 输出false 
  28.      */  
  29.     public static void test2(){  
  30.         String h = "hello";  
  31.         String hw = h + "world";  
  32.         String helloWolrd = "helloworld";  
  33.         p(hw == helloWolrd);  
  34.     }  
  35.     /** 
  36.      * String hw = h + "world";虽然包含变量 h 的运算,但是编译器 
  37.      * 对fianl变量在编译期能确定其值,会发生宏替换,即:h变量替换成其值"hello", 
  38.      * 然后编译器会对直接量字串直接合并 
  39.      * String hw = h + "world";在编译完后就变成了 String hw = "helloworld"; 
  40.      *  
  41.      * 输出 true 
  42.      * 
  43.      */  
  44.     public static void test3(){  
  45.         final String h = "hello";  
  46.         String hw = h + "world";  
  47.         String helloWolrd = "helloworld";  
  48.         p(hw == helloWolrd);  
  49.     }  
  50.     private static void p(boolean flag){  
  51.         System.out.println(flag);  
  52.     }  
  53. }  

 

 

 

1.2StringBuilder 和StirngBuffer


jdk5tiger以前,对于字符串拼接,例如:String hw = h + "wolrd",会产生临时变量,占用大量内存空间,需要程序员将 "+" 拼接改写成 StringBuffer.append();

 

java5提供了一个新的类用于字符串拼接StringBuilder,和StringBuffer略有区别,StringBuffer的方法是同步的,StringBuilder是非同步的。另外,java5的编译器会将 "+" 的字串,编译成StringBuilder.append(),也就是说,String str = a + b + c +d;这样的写法是不存在效率问题的,编译器帮你干了。

 

如无线程安全的要求,应该选择StringBuilder。

 

注意,编译器不是万能的

Java代码  收藏代码
  1. //这样的写法在运行期,在循环内将会产生大量的 StringBuilder对象的实例,效率低下  
  2. String str = "";  
  3. for(int i = 0;i < 100000;i++){  
  4.     str += i;  
  5. }  
  6. //应该改成下面的写法  
  7. StringBuilder sb = new StringBuilder();  
  8. for(int i = 0;i < 100000;i++){  
  9.     sb.append(i);  
  10. }  

 

另外:StringBuilder有一个属性:容量,看下面构造函数

Java代码  收藏代码
  1.    /** 
  2.     * Constructs a string builder with no characters in it and an  
  3.     * initial capacity of 16 characters.  
  4.     */  
  5.    public StringBuilder() {  
  6. super(16);  
  7.    }  
  8.   
  9.    /** 
  10.     * Constructs a string builder with no characters in it and an  
  11.     * initial capacity specified by the <code>capacity</code> argument.  
  12.     * 
  13.     * @param      capacity  the initial capacity. 
  14.     * @throws     NegativeArraySizeException  if the <code>capacity</code> 
  15.     *               argument is less than <code>0</code>. 
  16.     */  
  17.    public StringBuilder(int capacity) {  
  18. super(capacity);  
  19.    }  

  StringBuilder的底层实现是char[]存储字串值,属性容量就是这个char[]数组的长度,默认是16,调用 append()方法时候,会先检查 (原字串长度+append新串长度) > 容量,如果超出,则是数组扩容为 (原字串长度+append新串长度 + 1) *2

 

结论:

(1)String str = "a" + "b" + "c";这样的直接量拼接,JVM将其视为String str = "abc";

(2)final变量的使用,同样也是有效率的。

(3)含有变量的拼接字串,String str = h + "world",在JDK5以上,大多数是不需要程序员改成StringBuilder或者StringBuffer的,但是在某些情况下是需要程序员写成StringBuilder/StringBuffer 

(4)如果是大字串,需要多次调用 append,最好指定StringBuider 的容量,就像这样new StirngBuilder(1024),指定容量不要太大也不要太小,最好能预知构建完字串的长度,实在不知道也可以先预估一下

2、截取字符串

Java代码  收藏代码
  1. public static void main(String[] args) {  
  2.     List<String> list = new ArrayList<String>();  
  3.     for(int i = 0;i < 100000;i++){  
  4.         String bigString = new String(new char[20*1024*1024]);  
  5.         String subStr = bigString.substring(1, 5);  
  6.         list.add(subStr);  
  7.         System.out.println("第几次" + i);  
  8.     }  
  9.     /* 
  10.      *第几次0 
  11.     第几次1 
  12.     第几次2 
  13.     第几次3 
  14.     Exception in thread "main" java.lang.OutOfMemoryError: Java heap space 
  15.         at java.util.Arrays.copyOf(Arrays.java:2882) 
  16.         at java.lang.StringValue.from(StringValue.java:24) 
  17.         at java.lang.String.<init>(String.java:178) 
  18.         at StringTest.main(StringTest.java:9)  
  19.      */  
  20. }  

 指定-Xmx10m时,JDK1.6 中,上面的代码就溢出了,因为substring(int,int)

看源码

Java代码  收藏代码
  1.    public String substring(int beginIndex, int endIndex) {  
  2. if (beginIndex < 0) {  
  3.     throw new StringIndexOutOfBoundsException(beginIndex);  
  4. }  
  5. if (endIndex > count) {  
  6.     throw new StringIndexOutOfBoundsException(endIndex);  
  7. }  
  8. if (beginIndex > endIndex) {  
  9.     throw new StringIndexOutOfBoundsException(endIndex - beginIndex);  
  10. }  
  11. return ((beginIndex == 0) && (endIndex == count)) ? this :  
  12.     new String(offset + beginIndex, endIndex - beginIndex, value);  
  13.    }  

 

也就是说,String subStr = bigString.substring(1, 5);strStr的char[]属性值和bigString的char[]属性值是相同的,所以subStr所占的内存空间和bigString的内存空间也是相同的,虽然subString只能显示出几个字符 ,subStr这个实例的各属性值如下:

  private final char value[] = bigString.value; 
  private final int offset = 1; 
  private final int count = 5; 
  private int hash;

解决:

Java代码  收藏代码
  1. String subStr = new String(bigString.substring(1, 5));  

 这样,就不会溢出了

 

还有一个解决办法,换成jdk7,则无此泄露点

 

结论:若是从大字串中截取出较小字串,应使用new String(bigString.substring(1, 5))构建新的字串

 

http://www.ibm.com/developerworks/cn/java/j-lo-optmizestring/#_ 表 _1._Java

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics