String和StringBuffer在字符串拼接性能上的比较是Java技术面试的常见问题,在很多书籍和文章中只是简单的说:“String是不可变类型,StringBuffer是可变类型;所以在字符串拼接时StringBuffer的效率要远高于String。”事实真的是这样吗?
1. 在只有字符串常量参与拼接的情况下,String的效率要远高于StringBuffer
package test; public class StringTest { public static void main(String[] args) { long time1 = System.nanoTime(); String hql = " select ui.real_name borrowerName,u.mobile mobile, u.email email,u.id borrowerId, " + " l.loan_title loanTitle,l.open_amount openAmount,l.contact_amount contactAmount," + " l.interest interest,l.loan_months loanMonths,p.name productName,l.loan_status loanStatus," + " l.borrow_type borrowType, l.repayment_type repaymentType,l.id id,due.leaveAmount leaveAmount," + " due.leaveNum leaveNum,c.channel_zn_name channelName" + " from loan l left join user_info ui on l.borrower_id=ui.user_id" + " left join user u on l.borrower_id=u.id" + " left join product p on l.product_id=p.id" + " left join channel c on c.channel_en_name=u.utm_source" + " right join (select brr.loan_id loanId, sum(brr.repay_principal_interest) leaveAmount,count(id) leaveNum " + " from borrow_repay_record brr where brr.status=:status or DATE_FORMAT(brr.repay_date,'%y-%m-%d') >= DATE_FORMAT(:leave_now,'%y-%m-%d') " + " GROUP BY brr.loan_id ) due on due.loanId=l.id " + " where l.loan_status=:loan_status "; long time2 = System.nanoTime(); StringBuffer sb = new StringBuffer(); sb.append("select ui.real_name borrowerName,u.mobile mobile, u.email email,u.id borrowerId,") .append("l.loan_title loanTitle,l.open_amount openAmount,l.contact_amount contactAmount," ) .append("l.interest interest,l.loan_months loanMonths,p.name productName,l.loan_status loanStatus," ) .append("l.borrow_type borrowType, l.repayment_type repaymentType,l.id id,due.leaveAmount leaveAmount," ) .append("due.leaveNum leaveNum,c.channel_zn_name channelName" ) .append("from loan l left join user_info ui on l.borrower_id=ui.user_id" ) .append("left join user u on l.borrower_id=u.id" ) .append("left join product p on l.product_id=p.id" ) .append("left join channel c on c.channel_en_name=u.utm_source" ) .append("right join (select brr.loan_id loanId, sum(brr.repay_principal_interest) leaveAmount,count(id) leaveNum") .append("from borrow_repay_record brr where brr.status=:status or DATE_FORMAT(brr.repay_date,'%y-%m-%d') >= DATE_FORMAT(:leave_now,'%y-%m-%d') ") .append("GROUP BY brr.loan_id ) due on due.loanId=l.id") .append("where l.loan_status=:loan_status"); long time3 = System.nanoTime(); System.out.println(time2-time1); System.out.println(time3-time2); } }
运行结果
8397 56915
为什么会出现String的效率远高于StringBuffer的情况?这不是和书上说的相反了吗?
实际上这里是因为Java在编译java文件到class文件时,进行了优化,查看class文件:
// Compiled from StringTest.java (version 1.6 : 50.0, super bit) public class test.StringTest { // Method descriptor #6 ()V // Stack: 1, Locals: 1 public StringTest(); 0 aload_0 [this] 1 invokespecial java.lang.Object() [8] 4 return Line numbers: [pc: 0, line: 3] Local variable table: [pc: 0, pc: 5] local: this index: 0 type: test.StringTest // Method descriptor #15 ([Ljava/lang/String;)V // Stack: 5, Locals: 9 public static void main(java.lang.String[] args); 0 invokestatic java.lang.System.nanoTime() : long [16] 3 lstore_1 [time1] 4 ldc <String " select ui.real_name borrowerName,u.mobile mobile, u.email email,u.id borrowerId, l.loan_title loanTitle,l.open_amount openAmount,l.contact_amount contactAmount, l.interest interest,l.loan_months loanMonths,p.name productName,l.loan_status loanStatus, l.borrow_type borrowType, l.repayment_type repaymentType,l.id id,due.leaveAmount leaveAmount, due.leaveNum leaveNum,c.channel_zn_name channelName from loan l left join user_info ui on l.borrower_id=ui.user_id left join user u on l.borrower_id=u.id left join product p on l.product_id=p.id left join channel c on c.channel_en_name=u.utm_source right join (select brr.loan_id loanId, sum(brr.repay_principal_interest) leaveAmount,count(id) leaveNum from borrow_repay_record brr where brr.status=:status or DATE_FORMAT(brr.repay_date,'%y-%m-%d') >= DATE_FORMAT(:leave_now,'%y-%m-%d') GROUP BY brr.loan_id ) due on due.loanId=l.id where l.loan_status=:loan_status "> [22] 6 astore_3 [hql] 7 invokestatic java.lang.System.nanoTime() : long [16] 10 lstore 4 [time2] 12 new java.lang.StringBuffer [24] 15 dup 16 invokespecial java.lang.StringBuffer() [26] 19 astore 6 [sb] 21 aload 6 [sb] 23 ldc <String "select ui.real_name borrowerName,u.mobile mobile, u.email email,u.id borrowerId,"> [27] 25 invokevirtual java.lang.StringBuffer.append(java.lang.String) : java.lang.StringBuffer [29] 28 ldc <String "l.loan_title loanTitle,l.open_amount openAmount,l.contact_amount contactAmount,"> [33] 30 invokevirtual java.lang.StringBuffer.append(java.lang.String) : java.lang.StringBuffer [29] 33 ldc <String "l.interest interest,l.loan_months loanMonths,p.name productName,l.loan_status loanStatus,"> [35] 35 invokevirtual java.lang.StringBuffer.append(java.lang.String) : java.lang.StringBuffer [29] 38 ldc <String "l.borrow_type borrowType, l.repayment_type repaymentType,l.id id,due.leaveAmount leaveAmount,"> [37] 40 invokevirtual java.lang.StringBuffer.append(java.lang.String) : java.lang.StringBuffer [29] 43 ldc <String "due.leaveNum leaveNum,c.channel_zn_name channelName"> [39] 45 invokevirtual java.lang.StringBuffer.append(java.lang.String) : java.lang.StringBuffer [29] 48 ldc <String "from loan l left join user_info ui on l.borrower_id=ui.user_id"> [41] 50 invokevirtual java.lang.StringBuffer.append(java.lang.String) : java.lang.StringBuffer [29] 53 ldc <String "left join user u on l.borrower_id=u.id"> [43] 55 invokevirtual java.lang.StringBuffer.append(java.lang.String) : java.lang.StringBuffer [29] 58 ldc <String "left join product p on l.product_id=p.id"> [45] 60 invokevirtual java.lang.StringBuffer.append(java.lang.String) : java.lang.StringBuffer [29] 63 ldc <String "left join channel c on c.channel_en_name=u.utm_source"> [47] 65 invokevirtual java.lang.StringBuffer.append(java.lang.String) : java.lang.StringBuffer [29] 68 ldc <String "right join (select brr.loan_id loanId, sum(brr.repay_principal_interest) leaveAmount,count(id) leaveNum"> [49] 70 invokevirtual java.lang.StringBuffer.append(java.lang.String) : java.lang.StringBuffer [29] 73 ldc <String "from borrow_repay_record brr where brr.status=:status or DATE_FORMAT(brr.repay_date,'%y-%m-%d') >= DATE_FORMAT(:leave_now,'%y-%m-%d') "> [51] 75 invokevirtual java.lang.StringBuffer.append(java.lang.String) : java.lang.StringBuffer [29] 78 ldc <String "GROUP BY brr.loan_id ) due on due.loanId=l.id"> [53] 80 invokevirtual java.lang.StringBuffer.append(java.lang.String) : java.lang.StringBuffer [29] 83 ldc <String "where l.loan_status=:loan_status"> [55] 85 invokevirtual java.lang.StringBuffer.append(java.lang.String) : java.lang.StringBuffer [29] 88 pop 89 invokestatic java.lang.System.nanoTime() : long [16] 92 lstore 7 [time3] 94 getstatic java.lang.System.out : java.io.PrintStream [57] 97 lload 4 [time2] 99 lload_1 [time1] 100 lsub 101 invokevirtual java.io.PrintStream.println(long) : void [61] 104 getstatic java.lang.System.out : java.io.PrintStream [57] 107 lload 7 [time3] 109 lload 4 [time2] 111 lsub 112 invokevirtual java.io.PrintStream.println(long) : void [61] 115 return Line numbers: [pc: 0, line: 6] [pc: 4, line: 7] [pc: 7, line: 21] [pc: 12, line: 23] [pc: 21, line: 24] [pc: 28, line: 25] [pc: 33, line: 26] [pc: 38, line: 27] [pc: 43, line: 28] [pc: 48, line: 29] [pc: 53, line: 30] [pc: 58, line: 31] [pc: 63, line: 32] [pc: 68, line: 33] [pc: 73, line: 34] [pc: 78, line: 35] [pc: 83, line: 36] [pc: 89, line: 38] [pc: 94, line: 40] [pc: 104, line: 41] [pc: 115, line: 42] Local variable table: [pc: 0, pc: 116] local: args index: 0 type: java.lang.String[] [pc: 4, pc: 116] local: time1 index: 1 type: long [pc: 7, pc: 116] local: hql index: 3 type: java.lang.String [pc: 12, pc: 116] local: time2 index: 4 type: long [pc: 21, pc: 116] local: sb index: 6 type: java.lang.StringBuffer [pc: 94, pc: 116] local: time3 index: 7 type: long }
在main方法的第四行很容易看出,编译时Java把多个字符串常量拼接成了一个长字符串,效率自然比使用StringBuffer高得多。
2. 有变量参与字符串拼接时,StringBuffer的效率要高于String
3. 对于有大量字符串常量参与拼接时,可以将大段的字符串常量先用String的+进行拼接,再和其他部分使用StringBuffer进行拼接。
package test; public class StringTest2 { public static void main(String[] args) { long time1 = System.nanoTime(); String sql = " SELECT llr.id,llr.finance_plan_subpoint_id as financePlanSubPointId," + " llr.loan_Id as loanId,llr.lend_amount as lendAmount,l.interest" + " FROM loan_lender_record llr JOIN loan l ON llr.loan_id = l.id " + " WHERE llr.finance_plan_subpoint_id IN( SELECT id " + " FROM finance_plan_subpoint fps " + " WHERE fps.finance_plan_id = " + System.nanoTime() + ")"; long time2 = System.nanoTime(); StringBuffer sb = new StringBuffer(); sb.append(" SELECT llr.id,llr.finance_plan_subpoint_id as financePlanSubPointId,") .append(" llr.loan_Id as loanId,llr.lend_amount as lendAmount,l.interest" ) .append(" FROM loan_lender_record llr JOIN loan l ON llr.loan_id = l.id " ) .append(" WHERE llr.finance_plan_subpoint_id IN( SELECT id " ) .append(" FROM finance_plan_subpoint fps " ) .append(" WHERE fps.finance_plan_id = " ) .append(System.nanoTime()) .append(")"); long time3 = System.nanoTime(); String tempSql = " SELECT llr.id,llr.finance_plan_subpoint_id as financePlanSubPointId," + " llr.loan_Id as loanId,llr.lend_amount as lendAmount,l.interest" + " FROM loan_lender_record llr JOIN loan l ON llr.loan_id = l.id " + " WHERE llr.finance_plan_subpoint_id IN( SELECT id " + " FROM finance_plan_subpoint fps " + " WHERE fps.finance_plan_id = "; StringBuffer sb1 = new StringBuffer(tempSql); sb1.append(System.nanoTime()).append(")"); long time4 = System.nanoTime(); System.out.println(time2-time1); System.out.println(time3-time2); System.out.println(time4-time3); } }
运行结果:
52249 41520 8397
4. 使用StringBuffer拼接字符串时,适当的直接初始化大小可以提升效率
StringBuffer的底层是char数组,默认长度是16;当长度不足时按照“新长度=(原长度+1)*2” 来增大。
// StringBuffer增大长度的实现 void expandCapacity(int minimumCapacity) { int newCapacity = (value.length + 1) * 2; if (newCapacity < 0) { newCapacity = Integer.MAX_VALUE; } else if (minimumCapacity > newCapacity) { newCapacity = minimumCapacity; } value = Arrays.copyOf(value, newCapacity); }
如果字符串拼接的结果比较大,在使用StringBuffer时就会多次调用此函数,影响效率。所以可以直接在初始化时,设置好StringBuffer的长度。
字符串拼接的编码原则:
1. 全部由字符串常量组成的字符串进行拼接, 直接使用String的+操作即可。
2. 当有字符串变量参与拼接时,应当使用StringBuffer进行拼接操作。
3. 对于既有很多字符串常量也有字符串变量参与拼接,可以将大段的字符串常量先用String的+进行拼接,再和其他部分使用StringBuffer进行拼接。
4. 使用StringBuffer时,如果可以预估出结果长度,应该设置StringBuffer的初始化长度。
相关推荐
在字符串连接操作中,StringBuffer 的效率要比 String 高。例如,String str = new String("welcome to ");str += "here";的处理步骤实际上是通过建立一个 StringBuffer,让它调用 append(),最后再将 StringBuffer ...
* 使用 StringBuffer 类型:在字符串对象经常改变的情况下,使用 StringBuffer 类型可以提高性能和线程安全性。 * 使用 StringBuilder 类型:在单线程环境下,使用 StringBuilder 类型可以提高性能,但需要额外的...
在Java编程语言中,`String`和`StringBuffer`都是用来表示和操作字符串的重要类,但它们在性能和使用场景上有显著的区别。 首先,`String`类是不可变的,这意味着一旦创建了一个`String`对象,它的内容就不能改变。...
在Java中,字符串是我们经常使用的数据类型,而String、StringBuffer和StringBuilder是Java中三种常用的字符串类。在这篇文章中,我们将从源码角度对String、StringBuffer和StringBuilder进行深入分析,了解它们之间...
首先,`StringBuffer`或`StringBuilder`在Java中用于构建可变字符串,它们在循环中拼接字符串时比直接使用`+`操作符更有效率。然而,这种字符串拼接方式在处理大量数据或者频繁拼接时,容易导致内存开销过大,因为...
在Java编程语言中,String、StringBuilder和StringBuffer都是用来处理字符串的类,它们之间存在一些重要的区别,主要涉及到性能和线程安全性。 首先,`String`类代表的是字符串常量,一旦创建,其内容就不能改变。...
JAVA平台提供了两个类:String和StringBuffer,它们可以储存和操作字符串,即包含多个字符的字符数据.这个String类提供了数值不可改变的字符串.而这个StringBuffer类提供的字符串进行修改.当你知道字符数据要改变的时候...
1. **单线程环境**:在这种情况下,推荐使用 **StringBuilder**,因为它不提供线程安全机制,所以在性能上优于 StringBuffer 和 String。 2. **多线程环境**: - 如果需要修改字符串内容,则应选择 **...
在Java编程语言中,`String`和`StringBuffer`都是用来表示和操作字符串的重要类,但它们在使用场景和性能上有显著的区别。了解这些差异对于编写高效、优化的代码至关重要。 首先,`String`类是不可变的。这意味着...
在Java编程语言中,`String`、`StringBuffer`和`StringBuilder`是处理字符串的三个重要类,它们各自有特定的使用场景和优缺点。理解它们的差异对于编写高效的代码至关重要。 **String类** `String`是不可变的类,...
在Java中,处理字符串主要涉及到两个类:String和StringBuffer。这两个类虽然在功能上有许多相似之处,但它们在实现和性能上却有很大的差别。 首先我们来理解String。String类在Java中是不可变的,这意味着一旦一个...
在这个示例中,我们比较了使用 `String` 和 `StringBuffer` 进行 5000 次字符串拼接的执行时间。根据实际运行结果,我们可以发现 `StringBuffer` 的执行时间明显短于 `String`,尤其是在大量字符串拼接的情况下。 #...
本文主要探讨了在Java中几种不同的字符串拼接方法的性能差异,包括使用操作符`+`、`String.concat()`、`StringBuffer.append()`和`StringBuilder.append()`。以下是对这些方法的详细分析: 1. **字符串拼接操作符 ...
当需要对字符串进行频繁修改时,使用`String`会带来性能上的损耗,因为每次修改都会创建一个新的字符串对象。 #### `StringBuffer` `StringBuffer` 类则是可变的字符序列,这意味着可以在不创建新对象的情况下改变...
在Java编程语言中,String和StringBuffer类都用于处理字符串,但它们在处理字符串拼接时有着显著的区别。本文将详细解析这两个类在拼接字符串时的行为差异。 首先,我们来了解一下String类。String对象在Java中是不...
在Java编程语言中,`String`与`StringBuffer`是两个重要的字符串处理类,它们各自具有独特的特性和用途,尤其在面试或笔试中常被提及作为考察应聘者对Java基础知识掌握程度的重要知识点。 ### `String`类:不可变性...
从非常细致的角度分析了String和Stringbuffer的主要区别
String:字符串常量 StringBuffer:字符创变量 StringBuilder:字符创变量 从上面的名字可以看到,String是“字符创常量”,也就是不可改变的对象。对于这句话的理解你可能会产生这样一个疑问 ,比如这段...
在Java编程语言中,`String`、`StringBuffer`和`StringBuilder`都是用来处理字符串的类,但它们之间存在显著的差异,主要体现在性能、线程安全性和使用场景上。 首先,`String`是最基本的字符串类,它代表的是不可...