字符串拼接问题应该是每个Java程序员都熟知的事情了,几乎每个Java程序员都读过关于StringBuffer/StringBuilder来拼接字符串。

在大多数的教程中,也许你会看到用+号拼接字符串会生成多个String,导致性能过差,建议使用StringBuffer/StringBuilder来拼接。

可是真的是这样的吗?

本文在JDK8中做了如下实验:

public static void main(String[] args) {
String result = "";
result += "some more data";
System.out.println(result);
}

  

通过javap -c来反编译得到:

Code:
0: aload_0 // Push 'this' on to the stack
1: invokespecial #1 // Invoke Object class constructor
// pop 'this' ref from the stack
4: return // Return from constructor public static void main(java.lang.String[]);
Code:
0: ldc #2 // Load constant #2 on to the stack
2: astore_1 // Create local var from stack (pop #2)
3: new #3 // Push new StringBuilder ref on stack
6: dup // Duplicate value on top of the stack
7: invokespecial #4 // Invoke StringBuilder constructor
// pop object reference
10: aload_1 // Push local variable containing #2
11: invokevirtual #5 // Invoke method StringBuilder.append()
// pop obj reference + parameter
// push result (StringBuilder ref)
14: ldc #6 // Push "some more data" on the stack
16: invokevirtual #5 // Invoke StringBuilder.append
// pop twice, push result
19: invokevirtual #7 // Invoke StringBuilder.toString:();
22: astore_1 // Create local var from stack (pop #6)
23: getstatic #8 // Push value System.out:PrintStream
26: aload_1 // Push local variable containing #6
27: invokevirtual #9 // Invoke method PrintStream.println()
// pop twice (object ref + parameter)
30: return // Return void from method

  

可以看到Java编译器优化了生成的字节码,自动创建了一个StringBuilder,并进行append操作。

由于构建最终字符串的子字符串在编译时已经已知了,在这种情况下Java编译器才会进行如上的优化。这种优化称为a static string concatenation optimization,自JDK5时就开始启用。

那是否就能说明在JDK5以后,我们不再需要手动生成StringBuilder,通过+号也能达到同样的性能?

我们尝试下动态拼接字符串:

动态拼接字符串指的是仅在运行时才知道最终字符串的子字符串。比如在循环中增加字符串:

public static void main(String[] args) {
String result = "";
for (int i = 0; i < 10; i++) {
result += "some more data";
}
System.out.println(result);
}

  

同样反编译:

Code:
0: aload_0 // Push 'this' on to the stack
1: invokespecial #1 // Invoke Object class constructor
// pop 'this' ref from the stack
4: return // Return from constructor public static void main(java.lang.String[]);
Code:
0: ldc #2 // Load constant #2 on to the stack
2: astore_1 // Create local var from stack, pop #2
3: iconst_0 // Push value 0 onto the stack
4: istore_2 // Pop value and store it in local var
5: iload_2 // Push local var 2 on to the stack
6: i2d // Convert int to double on
// top of stack (pop + push)
7: ldc2_w #3 // Push constant 10e6 on to the stack
10: dcmpg // Compare two doubles on top of stack
// pop twice, push result: -1, 0 or 1
11: ifge 40 // if value on top of stack is greater
// than or equal to 0 (pop once)
// branch to instruction at code 40
14: new #5 // Push new StringBuilder ref on stack
17: dup // Duplicate value on top of the stack
18: invokespecial #6 // Invoke StringBuilder constructor
// pop object reference
21: aload_1 // Push local var 1 (empty String)
// on to the stack
22: invokevirtual #7 // Invoke StringBuilder.append
// pop obj ref + param, push result
25: ldc #8 // Push "some more data" on the stack
27: invokevirtual #7 // Invoke StringBuilder.append
// pop obj ref + param, push result
30: invokevirtual #9 // Invoke StringBuilder.toString
// pop object reference
33: astore_1 // Create local var from stack (pop)
34: iinc 2, 1 // Increment local variable 2 by 1
37: goto 5 // Move to instruction at code 5
40: getstatic #10 // Push value System.out:PrintStream
43: aload_1 // Push local var 1 (result String)
44: invokevirtual #11 // Invoke method PrintStream.println()
// pop twice (object ref + parameter)
47: return // Return void from method

  

可以看到在14的时候new了StringBuilder,但是在37的时候goto到了5,在循环过程中,并没有达到最优化,不断在生成新的StringBuilder。

所以上述代码类似:

String result = "";
for (int i = 0; i < 10; i++) {
StringBuilder tmp = new StringBuilder();
tmp.append(result);
tmp.append("some more data");
result = tmp.toString();
}
System.out.println(result);

  

可以看到不断生成新的StringBuilder,并且通过tostring,原来的StringBuilder将不再引用,作为垃圾,也增加了GC成本。

所以,在实际的使用中,当你无法区分字符串是静态拼接还是动态拼接的时候,还是使用StringBuilder吧。

Reference:
http://www.pellegrino.link/2015/08/22/string-concatenation-with-java-8.html

来源:开源中国---Hosee
链接:https://my.oschina.net/hosee/blog/1786130

更多干货可关注公众号,回复“文档”获取

 

谈谈JDK8中的字符串拼接的更多相关文章

  1. Java中的字符串拼接

    Java中的字符串拼接 1.设计源码 /** * @Title:IndexOf.java * @Package:com.you.freemarker.model * @Description: * @ ...

  2. ||在oracle数据库中起到字符串拼接的作用

    例子:select org.id from org where inner_code like '12011601001' || '%' ||在oracle数据库中起到字符串拼接的作用,上面等同于'1 ...

  3. 从源代码的角度聊聊java中StringBuffer、StringBuilder、String中的字符串拼接

    长久以来,我们被教导字符串的连接最好用StringBuffer.StringBuilder,但是我们却不知道这两者之间的区别.跟字符串相关的一些方法中总是有CharSequence.StringBuf ...

  4. golang中的字符串拼接

    go语言中支持的字符串拼接的方法有很多种,这里就来罗列一下 常用的字符串拼接方法 1.最常用的方法肯定是 + 连接两个字符串.这与python类似,不过由于golang中的字符串是不可变的类型,因此用 ...

  5. sql中的字符串拼接

    转载自:https://www.cnblogs.com/rainman/p/6203065.html 1. 概述 在SQL语句中经常需要进行字符串拼接,以sqlserver,oracle,mysql三 ...

  6. C#中的字符串拼接@,$

    转载自:https://blog.csdn.net/qq_40666620/article/details/101695138 一:@ @的意思是以@标注的字符出,其中所有的符号均为字符串符号,没有什 ...

  7. oracle存储过程中使用字符串拼接

    1.使用拼接符号“||” v_sql := 'SELECT * FROM UserInfo WHERE ISDELETED = 0 AND ACCOUNT =''' || vAccount || '' ...

  8. javascript中字符串拼接详解

    字符串拼接是所有程序设计语言都需要的操作.当拼接结果较长时,如何保证效率就成为一个很重要的问题.本文介绍的是Javascript中的字符串拼接,希望对你有帮助,一起来看.   最近在研究<jav ...

  9. 阿里巴巴Java开发手册_不建议在循环体中使用+进行字符串拼接

    18. [推荐]循环体内,字符串的连接方式,使用StringBuilder的append方法进行扩展. 说明:下例中,反编译出的字节码文件显示每次循环都会new出一个StringBuilder对象,然 ...

随机推荐

  1. Transition FrameWork

    Android Transition Framework可以实现三种效果: 不同Activity之间切换时,Activityc的内容(contentView)转场动画 不同Activity之间切换时, ...

  2. python3+Appium自动化12-H5元素定位环境搭建

    前言 在混合开发的App中,经常会有内嵌的H5页面.那么这些H5页面元素该如何进行定位操作呢? 针对这种场景直接使用前面所讲的方法来进行定位是行不通的,因为前面的都是基于Andriod原生控件进行元素 ...

  3. PV、UV、VV,CV的含义

    其中VV和CV是播放类指标,PV和UV是浏览类指标. 1. 播放类指标 VV(Video View,播放数),是指在一个统计周期内,视频被打开的次数之和. CV(Connect Views,内容播放数 ...

  4. 04-spring中的aop演示

    1 xml配置 1 导包 2 准备对象 package www.test.service; public interface UserService { public abstract void ad ...

  5. 【LDAP】LDAP介绍

    原文:http://ldapman.org/articles/intro_to_ldap.html原文作者:Michael Donnelly 什么是LDAP? LDAP的英文全称是Lightweigh ...

  6. Oracle同义词、索引、分区

    同义词:是现有对象的一个别名 简化SQL语句 隐藏对象的名称和所有者 提供对对象的公共访问 同义词共有两种类型 私有同义词只能在其模式内访问,且不能与当前模式的对象同名 公有同义词可被所有的数据库用户 ...

  7. java 多线程操作(锁)

    1.对象的加锁及其操作 程序中单独的并发线程对同一对象进行操作的代码段,成为临界区.java语言中的临界区可以是一个语句块 或者方法,使用关键字synchronized进行标识. 对象锁:java平台 ...

  8. C#遍历匿名对象的所有属性、value

    Object obj = ,pwd=" }; //遍历匿名对象 foreach (System.Reflection.PropertyInfo p in obj.GetType().GetP ...

  9. .NET 后台动态添加GridView列

    BoundField bfColumn1 = new BoundField(); bfColumn1.DataField = "zbcompanyname"; bfColumn1. ...

  10. 【起航计划 011】2015 起航计划 Android APIDemo的魔鬼步伐 10 App->Activity->Reorder Activities 后退栈 Intent FLAG

    Reorder Activities 示例有四个相关的Activitives: ReorderOnLaunch, ReorderTwo,ReorderThree, ReorderFour.其中Reor ...