2019-09-05

作者来源1
作者来源2

一、字符串拼接方式:+和concat和StringBuilder

源码:

public static void main(String[] args) {

        String str = "";

        System.out.println("执行加号拼接:");
        long starttime = System.currentTimeMillis();
        System.out.println("执行拼接之前的时间:"+starttime);
        for (int i = 0; i < 50000; i++) {
            str += "a";
        }
        long latertime = System.currentTimeMillis();
        System.out.println("执行拼接之后的时间:"+latertime);
        System.out.println("用加号拼接所花费的时间:"+(latertime - starttime));
        System.out.println("result== " + str);
        System.out.println("--------------------------------");


        String str1 = "";
        System.out.println("执行concat拼接:");
        long starttime1 = System.currentTimeMillis();
        System.out.println("执行拼接之前的时间:"+starttime1);
        for (int i = 0; i < 50000; i++) {
            str1 = str1.concat("b");
        }
        long latertime1 = System.currentTimeMillis();
        System.out.println("执行拼接之后的时间:"+latertime1);
        System.out.println("用加号拼接所花费的时间:"+(latertime1 - starttime1));
        System.out.println("result=="+str1);
        System.out.println("--------------------------------");

        StringBuilder stringBuilder = new StringBuilder();
        System.out.println("执行StringBuilder拼接:");
        long starttime2 = System.currentTimeMillis();
        System.out.println("执行拼接之前的时间:"+starttime2);
        for (int i = 0; i < 50000; i++) {
            stringBuilder.append("c");
        }
        long latertime2 = System.currentTimeMillis();
        System.out.println("执行拼接之后的时间:"+latertime2);
        System.out.println("用加号拼接所花费的时间:"+(latertime2 - starttime2));
        System.out.println("result=="+stringBuilder.toString());


    }

执行结果图:


image.png

由此可见,StringBuilder最快,concat次之,加号拼接最慢。

二、为什么会出现这样的情况

2.1 "+"方法实现原理

内部实现采用StringBuilder的append方法进行追加,最终以toString方法转换成String字符串,比如:

String wechat = "Hollis";
String introduce = "每日更新Java相关技术文章";
String hollis = wechat + "," + introduce;

反编译后的结果如下,反编译工具为jad。

String wechat = "Hollis";
String introduce = "\u6BCF\u65E5\u66F4\u65B0Java\u76F8\u5173\u6280\u672F\u6587\u7AE0";
String hollis = (new StringBuilder()).append(wechat).append(",").append(introduce).toString();

通过查看反编译后的代码,我们可以发现,在拼接字符串常量中,是将String转成了StringBuilder后,使用其append方法进行处理,也就是说,,在java中使用“+”对字符串拼接,采用的原理是使用StringBuilder。
但是它与纯粹使用StringBuilder的append方法是不同的:
1、每次执行循环添加字符时,它都会创建一个StringBuilder对象,
2、每次执行完后都会调用toString方法将其转换为字符串
所以耗费了更多时间。

2.2 "concat"方法实现原理

concat源代码

    public String concat(String str) {
        int otherLen = str.length();//获取添加的字符串长度
        if (otherLen == 0) {//如果字符串长度为0,则返回字符串长度本身
            return this;
        }
        int len = value.length;//获取原字符串的字符数组的长度
        char buf[] = Arrays.copyOf(value, len + otherLen);//将原字符串的字符数组放到buff数组中
        str.getChars(buf, len);//追加的字符串转化成字符数组,添加到buf中
        return new String(buf, true);//产生一个新的字符串返回
    }

整体的实现过程就是完成对数组的拷贝,虽然在内存中处理是原子性操作,速度非常快,但是,最后的return语句创建一个新的String对象,也就是每次concat操作都会创建一个新的String对象,这也是限制concat方法速度的原因。

2.3 "append"方法实现原理

代码实现

    public AbstractStringBuilder append(String str) {
        if (str == null)//如果是null值,则把null作为字符串处理
            return appendNull();
        int len = str.length();
        ensureCapacityInternal(count + len);//追加后的字符数组长度是否超过当前值
        str.getChars(0, len, value, count);//将字符串复制到目标数组
        count += len;
        return this;
    }
    private AbstractStringBuilder appendNull() {
        int c = count;
        ensureCapacityInternal(c + 4);
        final char[] value = this.value;
        value[c++] = 'n';
        value[c++] = 'u';
        value[c++] = 'l';
        value[c++] = 'l'; //将null添加到char[] value数组中
        count = c; //将count的值初始化
        return this;//返回结果
    }
    private void ensureCapacityInternal(int minimumCapacity) {
        // overflow-conscious code
        if (minimumCapacity - value.length > 0) {
            value = Arrays.copyOf(value,
                    newCapacity(minimumCapacity));//加长数组空间,并作数组拷贝
        }
    }

append方法都在做字符数组的处理,加长,拷贝等,这些都是基本的数据处理,整个方法内并没有生成对象,只是最后执行了toString方法返回了一个对象而已

三、题外

String str = "My name is ";
str = str + "JTZen9";

相当于str = new StringBuilder(str).append("JTZen9").toString();
也就是说,该str = str + "JTZen9",语句执行完后,总共有三个对象

String str = "My name is " + "JTZen9";

JVM会直接把str作为一个对象,即“My name is JTZen9”

四、使用场景

1、大多数情况,我们使用“+”,符合编码习惯和我们的阅读
2、当在频繁进行字符串的运算(如拼接、替换、删除等),或者在系统性能临界的时候,我们可以考虑使用concat或append方法

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,372评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,368评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,415评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,157评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,171评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,125评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,028评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,887评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,310评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,533评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,690评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,411评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,004评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,812评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,693评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,577评论 2 353

推荐阅读更多精彩内容