字符串优化处理

字符串优化处理

一)、字符串的内部结构

1)、char数组

表示String的内容,所有字符串的超集。

2)、offset偏移

3)、count长度

注:String的真实内容由offset和count进行定位和截取。

二)、字符串的特性

1)、不变性

当一个对象被多线程共享,并且频繁使用时 ,可以省略同步和锁等待时间。

2)、字符串常量池

3)、String由final修饰不能被继承

三)、subString()方法的内存泄露

  1. subString(int beginIndex)

  2. subString(int beginIndex, int endIndex)

subString()源码剖析:

1)、JDK1.6版本实现:

通过offset = offset + beginIndex, count = endIndex - beginIndex,采用时间换空间的方式重新生成一个字符串。

new String(int offset, int count, char[] value);

每生成一个新的子字符串都是将原char[]value数组赋值到新的子字符串中,然后通过偏移量和长度来决定实际的取值。

此时,若有大量的长字符串进行截取就会出现内存泄露的情况。

改进:

new String(subString(0,1)):

重新生成一个字符串,此时字符串的value值则为子字符串的value值,而new String(offset, count , cahr[] value)中生成的字符串因为没有被使用,则被GC回收,解决了内存泄露的问题。

2)、在JDK1.7版本后对该功能进行了改进,解决了内存泄露的问题

new String(int offset, int count, char[] value)

实现:通过offset和count重新生成一个数组,并根据偏移量将原数组对应的数据复制到新数组中。

四)、字符串的分割和查找

字符串的三种分割方式:

1)、split(String regex)

2)、StringTokenier(String str, String delim)

    hasMoreTokens();

    nexToken();

3)、自定义字符串分割

通过indexOf(int ch)和subString()

三种分割方法的速度比较:

split < StringTokenier < 自定义

(参考书籍是这么说,但通过自测试发现自定义截取子符串耗时却长得多)

/**
 * 字符串分割
 */
public class StringSplit {
    public static void main(String[] args) {
        String orgStr = null;
        StringBuffer sb = new StringBuffer();
        for(int i = 0; i < 10000; i++){
            sb.append(i);
            sb.append(";");
        }
        orgStr = sb.toString();
        //对orgStr字符串进行分割
        Long start = System.currentTimeMillis();

        /**
         * 使用split分割
         */
        for(int i = 0; i < 10000; i++){
            orgStr.split(";");
        }
        //使用split分割耗时2655
        System.out.println(System.currentTimeMillis()-start);

        /**
         * 使用StringTokenier分割
         */
        StringTokenizer st = new StringTokenizer(orgStr,";");
        Long start1 = System.currentTimeMillis();
        for(int i = 0; i < 10000; i++){
            while(st.hasMoreElements()){
                st.nextToken();
            }
            st = new StringTokenizer(orgStr,";");
        }
        //使用StringTokenizer分割耗时2719秒,在使用个StringTokenizer分割时还有创建和销毁StringTokenizer对象的时
        // 因此总体来说,使用StringTokenizer的性能比split的性能要好
        //耗时2719
        System.out.println(System.currentTimeMillis()-start1);

        /**
         * 使用indexOf和subString 来分割
         */
        String temp = orgStr;
        Long start2 = System.currentTimeMillis();
        for(int i = 0; i < 10000; i++){
            while(true) {
                String splitStr = null;
                //获取第一个;所在的位置
                int j = temp.indexOf(';');
                if(j < 0){
                    break;
                }
                splitStr = temp.substring(0, j);
                temp = temp.substring(j + 1);
            }
            temp = orgStr;
        }
        //耗时413933
        System.out.println(System.currentTimeMillis()-start2);
    }
}

五)、StringBuffer和StringBuilder

字符串的累加

== 静态字符串的累加 ==:

String result = "String" + "and" + "String" + "append";

StringBuffer sb = new StringBuffer();
sb.append("String");
sb.append("and");
sb.append("String");
sb.append("append");

我们想象中result拼接程序的执行过程应该是Sting + and 两个字符串生一个Stringand,Stringand再个String结合生成StringandString,生成多个字符串实例,最后的到result拼接结果。

sb中程序执行过程是,产生一个StringBuffer实例,然后往sb容器中添加数据。

测试两端代码的执行时间,发现result拼接的耗时小于sb添加的耗时,为什么呢?

通过反编译发现,在编译时期,java虚拟机自动将String result = "String" + "and" + "String" + "append";编译成String result = "StringandStringappend";直接生成拼接结果对象,而sb还要进行append操作,因此,对于显示的(静态的)字符串拼接性能优于StringBuffere拼接。

== 变量字符串的累加 ==:

String str1 = "String";
String str2 = "and";
String str3 = "String";
String str4 = "append";
String result = str1 + str2 + str3 + str4;

StringBuffer sb = new StringBuffer();
sb.append("String");
sb.append("and");
sb.append("String");
sb.append("append");

测试两端代码的执行速度,发现两端代码的执行速度几乎一样

原因:

通过反编译发现代码段1 的编译结果为

String str1 = "String";
String str2 = "and";
String str3 = "String";
String str4 = "append";
String result = new StringBuffer(str1).append(str2).append(str3).append(str4).toString();

结论:java虚拟机自动在编译时期就对字符串的累加进行了优化,对于静态字符串的累加在编译时期自动合成一个单独的长字符串,对于变量字符串的累加,使用StringBuffer对象来完成累加操作。

另一种字符串累加方式:

String str1 = "String";
String str2 = "and";
String str3 = "String";
String str4 = "append";
String result = str1.concat(str2).concat(str3).concat(str4);

字符串的累加效率:

+= / + < concat < StringBuffer/StringBuilder

StringBuffer和StringBuilder的区别:

1)、StringBuffer

线程同步,保证线程安全,但同步方法消耗一定的系统资源。

2)、StringBuilder

线程不同步,效率高,当线程不安全。

3)、StringBuffer和StringBuilder底层都是一个char[]类型的数组,默认数组长度为16个字节。

4)、当创建StringBuffer和StringBuilder对象时,为了提高系统的性能,最好声明对象的长度。

      因为,当所需长度大于内部数组的长度会会对内部数组进行扩容,并将原数据复制到扩容后的新数组中,存在大量的内存复制操作,消耗资源。

使用场景:不考虑线程安全时使用StringBuilder, 考虑线程安全时使用StringBuffer

猜你喜欢

转载自www.cnblogs.com/Auge/p/11646442.html