String拼接也有用加号更好的时候
做String拼接时用StringBuilder(或StringBuffer)好还是直接用+号性能好?一般来说是前者,不过也有用加号略好的时候。
首先我一直认为用+号有很好的可读性,而且当String拼接在一个等式时,即形如
String s = “abc” + s2 + s3
jdk的实现原理也是转换为一个StringBuilder并一直append,效率也是差不太多的,所以我是比较喜欢在无循环或条件分支代码的情况下全使用String相加,如果有循环或分支,就是写成这样:
StringBuilder hql =newStringBuilder(“select…………..”
+“ from”
+” where”);
If(xxxx){
Hql.append(“xxxx”);
}
不过大家的代码一般都是全append方式,代码一写可以写出几百行,写的时候不好写,改的时候不好读,真有点看不过去了。
今天特别对两种拼接方式做了一下测试,来给大家一个参考。
找到项目中一个约200行的hql拼接,将append全转化为+号,如图

publicstaticvoid testStringJoin(){
TestStringJoin instance =newTestStringJoin();
longbegin, elapse;
begin=System.currentTimeMillis();
int execTimes =10000;
for(int i =0; i < execTimes; i++){
instance.testStringJoinWithPlus();
}
elapse =System.currentTimeMillis()-begin;
System.out.println("testStringJoinWithPlus "+ execTimes
+" times elapse = "+ elapse +"ms");
begin=System.currentTimeMillis();
for(int i =0; i < execTimes; i++){
instance.testStringJoinWithStringBuilder();
}
elapse =System.currentTimeMillis()-begin;
System.out.println("testStringJoinWithStringBuilder "+ execTimes
+" times elapse = "+ elapse +"ms");
}
结果:
testStringJoinWithPlus 10000 times elapse = 77ms
testStringJoinWithStringBuilder 10000 times elapse = 151ms (这里有StringBuilder扩容问题,见下面继续分析)
基本上可保持后者约为前者2倍时间的情况,也就是说,用StringBuilder拼接字符串有时候还不如直接用+号拼接。
为啥呢?对比了一下两方法的中间代码,只取一小段就可以看出问题了
用StringBuilder拼接字符串的方式:
Code:
0:new#2; //class java/lang/StringBuilder
3: dup
4: sipush 5000
7: invokespecial #3; //Method java/lang/StringBuilder."<init>":(I)V
10: astore_1
11: aload_1
12: ldc #4; //String SELECT
14: invokevirtual #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
17: pop
18: aload_1
19: ldc #6; //String new map(
21: invokevirtual #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
24: pop
25: aload_1
26: ldc #7; //String corp.id AS corpId,
28: invokevirtual #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
31: pop
Code:
0:new#2; //class java/lang/StringBuilder
3: dup
4: invokespecial #85; //Method java/lang/StringBuilder."<init>":()V
7: ldc #86; //String SELECT new map( corp.id AS corpId, ( SELECT COUNT(*) FROM TPerson per, TXXJL jl WHERE per.CBh = jl.CBhPerson AND per.corpId = corp.id AND jl.CSJYJ IS NULL AND( jl.NSpcz = ? OR jl.NSpcz = ? OR( jl.NSpcz = ? AND jl.NSpjg = ? ) OR( jl.NSpcz = ? AND jl.NSpjg = ? ) ) AND( jl.CJdyj IS NULL OR jl.CJdyj = ? ) AND jl.CBh NOT IN( SELECT pctq.CBhJlxx FROM TXXPctq pctq, TXXPcxx pcxx WHERE pctq.CBhPcxx = pcxx.CBh AND pcxx.NLx = ? AND pcxx.CCjdw = corp.id ) )AS dcs, ( SELECT COUNT(*) FROM TPerson per, TXXJL jl WHERE per.CBh = jl.CBhPerson AND per.corpId = corp.id AND jl.CJdyj IS NULL AND( jl.NSpcz = ? OR jl.NSpcz = ? OR( jl.NSpcz = ? AND jl.NSpjg = ? ) OR( jl.NSpcz = ? AND jl.NSpjg = ? ) ) AND( jl.CSJYJ IS NULL OR jl.CSJYJ = ? ) AND jl.CBh NOT IN( SELECT pctq.CBhJlxx FROM TXXPctq pctq, TXXPcxx pcxx WHERE pctq.CBhPcxx = pcxx.CBh AND pcxx.NLx = ? AND pcxx.CCjdw = corp.id ) )AS dyyjd, ( SELECT COUNT(*) FROM TPerson per, TXXJL jl WHERE per.CBh = jl.CBhPerson AND per.corpId = corp.id AND( jl.CSJYJ = ? AND jl.CJdyj = ? ) AND( jl.NSpcz = ? OR jl.NSpcz = ? OR( jl.NSpcz = ? AND jl.NSpjg = ? ) OR( jl.NSpcz = ? AND jl.NSpjg = ? ) ) AND jl.CBh NOT IN( SELECT pctq.CBhJlxx FROM TXXPctq pctq, TXXPcxx pcxx WHERE pctq.CBhPcxx = pcxx.CBh AND pcxx.NLx = ? AND pcxx.CCjdw = corp.id ) )AS dfpqc, ( SELECT COUNT(*) FROM TXXJL jl, TZf zfxx, TXXPctq pctq WHERE jl.CBhPerson = zfxx.CBh AND pctq.CBhJlxx = jl.CBh AND pctq.CBhPcxx = ? AND jl.NSpzt = ? AND jl.NSpcz != ? AND zfxx.corpId = corp.id )AS dspbw, ( SELECT COUNT(DISTINCT pt.CBhJlxx) FROM TXXPctq pt, TXXJL tq, TPerson per, TXXPcxx pc WHERE pc.NLx =? AND pc.CCjdw = corp.id AND pt.CBhJlxx = tq.CBh AND tq.CBhPerson = per.CBh AND pt.CBhPcxx = pc.CBh AND per.NSfyx =? AND per.NSfyx =
9: invokevirtual #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
12: getstatic #87; //Field SF_YES:Ljava/lang/Integer;
15: invokevirtual #88; //Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
18: ldc #66; //String AND tq.NTqlb IS NOT NULL
20: invokevirtual #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
23: ldc #67; //String AND tq.NSpzt >= ?
25: invokevirtual #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
28: ldc #68; //String AND tq.NSpzt <=?
30: invokevirtual #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
后者说明,jdk在编译期已经将用加号拼接的字符串理解为一个字符串,不会再去创建一个StringBuilder一个一个的连接上去,而前者全程使用StringBuilder.append,就是实打实的拼接了,没有给jdk一个优化的机会。
我测试使用的jdk版本是:jdk1.5.0_22
这种性能差异少有人提及,很多人讨论过String相加的问题,直接得出尽量用StringBuilder(或StringBuffer)的结论,而没有考虑到一直使用StringBuilder结构拼接字符串,除了不美观不易读外,还会有在性能上输给 +号拼接 的问题
究其原因,大家一般测试时都使用很少量的字符串拼接,不太符合实际场景,没有考虑到写代码时经常出现像上面的几百行append带来的影响。
当然,即便这样两者的性能差距仍然不大,而且也没有在循环中用+号连接与append连接的差距大,但还有一点就是前面说的全程append的可读性差得太多,用+号连接的可读性显然是更好的,难道不应该选择更好的方式吗?
还有一个好处
+" AND per.NSfyx = "+TestStringJoin.SF_YES
因为字符串+号拼接时如果不出现变量、非字符串常量的时候,编译期就直接认为是一个字符串了,同时因为有字符串缓冲池的存在,
于是,这种情况下两者的性能差异是:
testStringJoinWithPlus 10000 times elapse = 0ms
testStringJoinWithStringBuilder 10000 times elapse = 157ms
所以我非常推荐在遇到循环或条件分支的之前,写sql就用+号拼接并合理换行排版,参数尽量都用?绑定,优雅又高效。
append中用+号拼接String如何?
之前我跟别人说不推荐在append中使用+号连接,因为这样在append方法中可能又会引起一次new StrinBuilder,不过看来应该改改了。
经过上面的测试,上面的拼接字符串方法我写成仅两次append,可以预料到会有下面这个情况:
Code:
0:new#2; //class java/lang/StringBuilder
3: dup
4: invokespecial #85; //Method java/lang/StringBuilder."<init>":()V
7: astore_1
8: aload_1
9: ldc #90; //String SELECT new map(corp.id AS corpId, (SELECT COUNT(*) FROM TPerson per, TXXJL jl WHERE per.CBh = jl.CBhPerson AND per.corpId = corp.id AND jl.CSJYJ IS NULL AND (jl.NSpcz = ? OR jl.NSpcz = ? OR (jl.NSpcz = ? AND jl.NSpjg = ?) OR (jl.NSpcz = ? AND jl.NSpjg = ?)) AND (jl.CJdyj IS NULL OR jl.CJdyj = ?) AND jl.CBh NOT IN (SELECT pctq.CBhJlxx FROM TXXPctq pctq, TXXPcxx pcxx WHERE pctq.CBhPcxx = pcxx.CBh AND pcxx.NLx = ? AND pcxx.CCjdw = corp.id)) AS dcs, (SELECT COUNT(*) FROM TPerson per, TXXJL jl
11: invokevirtual #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
14: pop
15: aload_1
16: ldc #91; //String WHERE per.CBh = jl.CBhPerson AND per.corpId = corp.id AND jl.CJdyj IS NULL AND (jl.NSpcz = ? OR jl.NSpcz = ? OR (jl.NSpcz = ? AND jl.NSpjg = ?) OR (jl.NSpcz = ? AND jl.NSpjg = ?)) AND (jl.CSJYJ IS NULL OR jl.CSJYJ = ?) AND jl.CBh NOT IN (SELECT pctq.CBhJlxx FROM TXXPctq pctq, TXXPcxx pcxx WHERE pctq.CBhPcxx = pcxx.CBh AND pcxx.NLx = ? AND pcxx.CCjdw = corp.id)) AS dyyjd, (SELECT COUNT(*) FROM TPerson per, TXXJL jl WHERE per.CBh = jl.CBhPerson AND per.corpId = corp.id AND (jl.CSJYJ = ? AND jl.CJdyj = ?) AND (jl.NSpcz = ? OR jl.NSpcz = ? OR (jl.NSpcz = ? AND jl.NSpjg = ?) OR (jl.NSpcz = ? AND jl.NSpjg = ?)) AND jl.CBh NOT IN (SELECT pctq.CBhJlxx FROM TXXPctq pctq, TXXPcxx pcxx WHERE pctq.CBhPcxx = pcxx.CBh AND pcxx.NLx = ? AND pcxx.CCjdw = corp.id)) AS dfpqc, (SELECT COUNT(*) FROM TXXJL jl, TZf zfxx, TXXPctq pctq WHERE jl.CBhPerson = zfxx.CBh AND pctq.CBhJlxx = jl.CBh AND pctq.CBhPcxx = ? AND jl.NSpzt = ? AND jl.NSpcz != ? AND zfxx.corpId = corp.id) AS dspbw, (SELECT COUNT(DISTINCT pt.CBhJlxx) FROM TXXPctq pt, TXXJL tq, TPerson per, TXXPcxx pc WHERE pc.NLx = ? AND pc.CCjdw = corp.id AND pt.CBhJlxx = tq.CBh AND tq.CBhPerson = per.CBh AND pt.CBhPcxx = pc.CBh AND per.NSfyx = ? AND per.NSfyx = 1 AND tq.NTqlb IS NOT NULL AND tq.NSpzt >= ? AND tq.NSpzt <= ? AND tq.NSpcz = ? AND tq.NSpjg = ?) AS tbcl, (SELECT COUNT(DISTINCT pt.CBhJlxx) FROM TXXPcxx pc, TXXPctq pt, TXXJL tq, TZf zf WHERE pc.NLx = ? AND pc.CCjdw = corp.id AND pt.CBhJlxx = tq.CBh AND tq.CBhPerson = per.CBh AND pt.CBhPcxx = pc.CBh AND per.NSfyx = ? AND tq.NTqlb IS NOT NULL AND ((tq.NSpzt >= ? AND tq.NSpzt <= ? AND tq.NSpcz = ? AND tq.NSpjg = ?) O (tq.NSpzt = ? AND tq.NSpcz >= ?))) AS djwzx) FROM XfzxCorp corp
18: invokevirtual #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
21: pop
22: aload_1
23: invokevirtual #84; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
26: areturn
编译后发现没有生成多余的StringBuilder。
就是说,如果仅仅是因为换行,而不是加入了其他的变量、常量、函数等情况下,append中的字符串可以出现+号拼接。
所以亲们,把sql、hql其他啥啥字符串拼接写得优雅点好不。
最后测试一下StringBuilder扩容问题
在测试方法中,直接创建StrinBuilder时的代码也是无参数的,这里面虽然无差别,但细心的人应该会考虑一下这个问题。
+号连接方式,由于上面的结论很多字符串相加而中间没有变量、非字符串常量等因素时,相当于一个字符串,也就是说至少需要14个外加进来的东西,才会引起一次扩容;
而直接创建StrinBuilder时,有几次append就算占用了几个容量,因此这也是后者效率差的一个原因。
测试的sql为4789字节,使用new StringBuilder(5000)不会产生扩容问题,这种情况下看看顶楼两种代码的差距呢?
结果如下:
testStringJoinWithPlus 10000 times elapse = 66ms
testStringJoinWithStringBuilder 10000 times elapse = 85ms
后者性能好了不少,虽然数值相差不大,但按百分比来说效率提升约70%((151/85)-1),不过从理论上来说,不会超过+号连接字符串的情况
String拼接也有用加号更好的时候的更多相关文章
- Java中string拼接,StringBuilder,StringBuffer和+
Java中string拼接,StringBuilder,StringBuffer和+,到底哪个更合适? StringBuilder线程不安全,效率较线程安全的StringBuffer高.jdk1.5之 ...
- java String拼接的方法选择及性能分析
String 拼接的方法选择 在拼接静态字符串时,尽量用 +,因为通常编译器会对此做优化,如: String test = "this " + "is " + ...
- Flex String拼接
平时Flex String拼接的时候直接str+str2 今天就想看看Flex自带的函数好不好用,然后使用 str.concat(str2); Alert.show(str); 结果没有变化,才发现一 ...
- String拼接字符串效率低,你知道原因吗?
面试官Q1:请问为什么String用"+"拼接字符串效率低下,最好能从JVM角度谈谈吗? 对于这个问题,我们先来看看如下代码: public class StringTest { ...
- 选择合适的String拼接方法(这篇博客是我抄的)
package com.test; public class FreeFile { public static void main(String[] args) { // 加号拼接 String st ...
- 【java编程】String拼接效率探究
转载:https://maimai.cn/article/detail?fid=1139790318&efid=0-ey6pWIySdmkx82QO-OSw 字符串,是Java中最常用的一个数 ...
- jdk不同版本对String拼接的优化分析
1. 测试demo代码 测试循环中字符串拼接优化 public class ForTest { public static void main(String[] args) { String a = ...
- 理解String拼接,+运算符重载的实际效果
引申:一个常见的String的面试题 public static void main(String[] args) { // TODO Auto-generated method stub Strin ...
- c++ string 拼接 int错误
程序中用到字符串和int合成字符串,受java习惯的影响,直接进行了字符串与int的+操作,结果不正确.查了一下才明白问题所在,记录一下string str=”abc”+1;输出为:bc,因为”abc ...
随机推荐
- node里如何查看浏览器信息
'use strict'; let http = require(`http`); http.createServer((req, res) => { console.log(req.heade ...
- 怎么让自己的java系统使用支付接口
昨天花了好久的时间学习了支付接口的教,我看了前7集,就够用了,大家上网搜索一下传智播客在线支付还不错. 1.一开始有一个form表单 2.这个表单是他帮你写好的,有很多银行,银行的name都是特定的 ...
- ashx页面 “检测到有潜在危险的 Request.Form 值”的解决方法(控制单个处理程序不检测html标签)
如题: 使用web.config的configuration/location节点. 在configuration节点内新建一个location节点,注意这个节点和system.webserver那些 ...
- $(document).ready(function(){});不执行
这里可能会有以下几种原因,请你挨个排查: 1.jqury的文件一定要引入1.js文件的引用路径不正确,特别是使用了命名空间,容易造成路径错误,使用绝对路径看是否成功 2.某一些函数使用错误,举个例子, ...
- 使用AutoMapper实现Dto和Model之间自由转换
应用场景:一个Web应用通过前端收集用户的输入成为Dto,然后将Dto转换成领域模型并持久化到数据库中.另一方面,当用户请求数据时,我们又需要做相反的工作:将从数据库中查询出来的领域模型以相反的方式转 ...
- Linux sz rz
借助XShell,使用linux命令 root 账号登陆: su root 1.编译安装 wget http://www.ohse.de/uwe/releases/lrzsz-0.12.20.tar. ...
- String 方法
import java.lang.*; /** * 1.查找"asdfjvjadsffvaadfkfasaffdsasdffadsafafsafdadsfaafd" * 该字符串中 ...
- visualSVN server库迁移(转)
转自:http://blog.csdn.net/yuhuijun_1/article/details/9762683 首先,VisualSVN Server Manager,包含两个路径,一个是安装路 ...
- maven 常用插件总结
maven-javadoc-plugin (1) 说明:该插件生成项目的javadoc.对于构建jar目标,javadoc会首先生成并打包放入jar文件中. (2) 默认用法: pom.xml配置 & ...
- eval函数:分号的应用
eval("echo'hello world';"); ("参数;") eval函数把参数当做php代码来执行,参数后要有分号,最后还要另加一个分号 相当于: ...