如何选择使用String、StringBuffer与StringBuilder

20 篇文章 0 订阅

一、引子—字符串String是Java编程中使用概率最高的变量,也许你觉得没有什么可讲的,随手拈来,然而字符串的处理却尤其需要我们的关注,因为大量的字符串实例的随意创建,给系统的效率带来了很大的问题。
二、开始对比—比如下面我们来做一个测试,对比String类和StringBuffer的执行效率:
● String执行10000次累加

long start = System.currentTimeMillis();
String str = "";
for (int i = 0; i < 10000; i++) {
    str += "," + i;
}
long end = System.currentTimeMillis();
System.out.println(end - start);

执行的结果花费了699ms。

● 使用StringBuffer类来代替String类:

long start = System.currentTimeMillis();
StringBuffer str = new StringBuffer();
for (int i = 0; i < 10000; i++) {
    str.append(",").append(i);
}
long end = System.currentTimeMillis();
System.out.println(end - start);

运行共花费了5ms。
{附加—-使用StringBuilder的时候执行同样的10000次一共花费10ms}

三、初步分析原因—通过对比发现StringBuffer几乎不花费时间。这是因为,String对象的每一次累加,都会先将累加的字符串创建一个实例对象然后再累加,等于是创建了10000个实例。而StringBuffer每次都是修改的原有实例对象,只是创建了1个实例。创建实例需要申请内存地址、写入数据的过程,大量的这种操作就会消耗大量的CPU计算资源。
四深入分析原因—-StringBuffer,StringBuilder这么高效率,那我们不再使用String类就可以了,其实它们在不同的情况下各有选择的优势。String、StringBuffer与StringBuilder三者最大的区别是:
● String是字符串常量
● StringBuffer是字符串变量(线程安全)
● StringBuilder是字符串变量(非线程安全)
简要的说,String类型和StringBuffer类型的主要性能区别其实在于,String是不可变的对象,因此在每次对String类型进行改变的时候,其实都等同于生成了一个新的String对象,然后将指针指向新的String对象,所以经常改变内容的字符串最好不要用String,因为每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了以后,JVM的GC就会开始工作,那速度是一定会相当慢的。
而如果是使用StringBuffer类则结果就不一样了,每次结果都会对StringBuffer对象本身进行操作,而不是生成新的对象,再改变对象引用。所以在一般情况下我们推荐使用StringBuffer,特别是字符串对象经常改变的情况下。而在某些特别情况下,String对象的字符串拼接其实是被JVM解释成了StringBuffer对象的拼接,所以这些时候String对象的速度并不会比StringBuffer对象慢,而特别是以下的字符串对象生成中,String效率是远要比StringBuffer快的:
String str = “This is only a” + “ simple” + “ test”;
StringBuffer builder = new StringBuilder(“This is only a”).append(“ simple”).append(“ test”);
你会很惊讶的发现,生成str对象的速度简直太快了,而这个时候StringBuffer居然速度上根本一点都不占优势。其实这是JVM的一个把戏,实际上:
String str = “This is only a” + “ simple” + “test”;
其实就是:
String str = “This is only a simple test”;
所以不需要太多的时间了。但大家这里要注意的是,如果你的字符串是来自另外的String对象的话,速度就没那么快了,譬如:
String str2 = “This is only a”;
String str3 = “ simple”;
String str4 = “ test”;
String str1 = str2 +str3 + str4;
这时候JVM会规规矩矩的按照原来的方式去做。
为了测试这三个类的累加不同次数字符串时的效率,我们编写一个测试类,分别按次数累加字符串:

public class Test {
    static int[] arr= {1,10,100,1000,10000,100000,1000000};

    static int count;//循环次数

// 测试String
    public static void testString() {
        for(int j=0;j<arr.length;j++) {
            count=arr[j];
            long start = System.nanoTime();
            String str = "";
            for (int i = 0; i < count; i++) {
                str += "," + i;
            }
            long end = System.nanoTime();
            System.out.println("String  " +count+"次耗时:"+ (end - start)+" 微毫秒");
        }

    }

    // 测试StringBuffer
    public static void testStringBuffer() {
        for(int j=0;j<arr.length;j++) {
            count=arr[j];
            long start = System.nanoTime();
            StringBuffer str = new StringBuffer();
            for (int i = 0; i < count; i++) {
                str.append(",").append(i);
            }
            long end = System.nanoTime();
            System.out.println("StringBuffer:  " +count+"次花费:" + (end - start)+" 微毫秒");
        }

    }

    // 测试StringBuilder
    public static void testStringBuilder() {
        for(int j=0;j<arr.length;j++) {
            count=arr[j];
            long start = System.nanoTime();
            StringBuilder str = new StringBuilder();
            for (int i = 0; i < count; i++) {
                str.append(",").append(i);
            }
            long end = System.nanoTime();
            System.out.println("StringBuilder:  " +count+"次花费:" + (end - start)+" 微毫秒");
        }

    }

    public static void main(String[] args) {

        Test.testStringBuffer();
        Test.testStringBuilder();
        Test.testString();
    }
}

运行该程序执行的测试时间:
[root@localhost java]# java Test
StringBuffer: 1次花费:73336 微毫秒
StringBuffer: 10次花费:14422 微毫秒
StringBuffer: 100次花费:263772 微毫秒
StringBuffer: 1000次花费:1446821 微毫秒
StringBuffer: 10000次花费:10872059 微毫秒
StringBuffer: 100000次花费:13631487 微毫秒
StringBuffer: 1000000次花费:90372059 微毫秒
StringBuilder: 1次花费:2463 微毫秒
StringBuilder: 10次花费:4047 微毫秒
StringBuilder: 100次花费:22652 微毫秒
StringBuilder: 1000次花费:289265 微毫秒
StringBuilder: 10000次花费:1062742 微毫秒
StringBuilder: 100000次花费:14102631 微毫秒
StringBuilder: 1000000次花费:66190952 微毫秒
String 1次耗时:4270 微毫秒
String 10次耗时:78887 微毫秒
String 100次耗时:1519199 微毫秒
String 1000次耗时:18991478 微毫秒
String 10000次耗时:388491716 微毫秒
String 100000次耗时:41303264939 微毫秒
String 1000000次耗时:2419175189504 微毫秒

这里写图片描述
String在10w次循环就耗时41303ms,而在100万次的时候更是耗时约41分钟,而StringBuffer在100万次循环时间才为90ms,StringBuilder的时间为66ms。显然选择优先级为:StringBuilder>StringBuffer>String。因此,对于这三个类的使用,我们需要按照以下情况去选择:
● 如果你偶尔对简单的字符串常量进行拼接,那么可以使用String,它足够简单而且轻量级;
● 如果你需要经常进行字符串的拼接、累加操作,请使用StringBuffer或StringBuilder;
● 如果是在单线程的环境中,建议使用StringBuilder,它要比StringBuffer快;如果是在多线程的环境中,建议使用StringBuffer,它是线程安全的;
五得出结论—-StringBuilder实际上是我们的首选,只有在多线程时才可以考虑使用StringBuffer,只有在字符串的拼接足够简单时才使用String。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值