引言
-
第一种就是每次循环将对象中的几个字段拼接成一个新字段,再赋值给对象
-
第二种操作是在循环外创建一个字符串对象,每次循环向该字符串拼接新的内容。循环结束后得到拼接好的字符串
/**
* 循环内 String 拼接字符串,一次循环后销毁
*/
public
static
void
useString
(
)
{
for
(int i
=
0
; i
<
CYCLE_NUM_BIGGER
; i
++
)
{
String str
= str1
+ i
+ str2
+ i
+ str3
+ i
+ str4
;
}
}
/**
* 循环内 使用 StringBuilder 拼接字符串,一次循环后销毁
*/
public
static
void
useStringBuilder
(
)
{
for
(int i
=
0
; i
<
CYCLE_NUM_BIGGER
; i
++
)
{
StringBuilder sb
=
new
StringBuilder
(
)
;
String s
= sb
.
append
(str1
)
.
append
(i
)
.
append
(str2
)
.
append
(i
)
.
append
(str3
)
.
append
(i
)
.
append
(str4
)
.
toString
(
)
;
}
}
/**
* 多次循环拼接成一个字符串 用 String
*/
public
static
void
useStringSpliceOneStr
(
)
{
String str
=
""
;
for
(int i
=
0
; i
<
CYCLE_NUM_LOWER
; i
++
)
{
str
+= str1
+ str2
+ str3
+ str4
+ i
;
}
}
/**
* 多次循环拼接成一个字符串 用 StringBuilder
*/
public
static
void
useStringBuilderSpliceOneStr
(
)
{
StringBuilder sb
=
new
StringBuilder
(
)
;
for
(int i
=
0
; i
<
CYCLE_NUM_LOWER
; i
++
)
{
sb
.
append
(str1
)
.
append
(str2
)
.
append
(str3
)
.
append
(str4
)
.
append
(i
)
;
}
}
public
static int
executeSometime
(int kind
, int num
) throws InterruptedException
{
Thread
.
sleep
(
2000
)
;
int sum
=
0
;
for
(int i
=
0
; i
< num
+
5
; i
++
)
{
long begin
= System
.
currentTimeMillis
(
)
;
switch
(kind
)
{
case
1
:
useString
(
)
;
break
;
case
2
:
useStringBuilder
(
)
;
break
;
case
3
:
useStringSpliceOneStr
(
)
;
break
;
case
4
:
useStringBuilderSpliceOneStr
(
)
;
break
;
default
:
return
0
;
}
long end
= System
.
currentTimeMillis
(
)
;
if
(i
>
5
)
{
sum
+=
(end
- begin
)
;
}
}
return sum
/ num
;
}
public
class
StringTest
{
public
static final int
CYCLE_NUM_BIGGER
=
10_000_000
;
public
static final int
CYCLE_NUM_LOWER
=
10_000
;
public
static final String str1
=
"张三"
;
public
static final String str2
=
"李四"
;
public
static final String str3
=
"王五"
;
public
static final String str4
=
"赵六"
;
public
static
void
main
(String
[
] args
) throws InterruptedException
{
int time
=
0
;
int num
=
5
;
time
=
executeSometime
(
1
, num
)
;
System
.out
.
println
(
"String拼接 "
+
CYCLE_NUM_BIGGER
+
" 次,"
+ num
+
"次平均时间:"
+ time
+
" ms"
)
;
time
=
executeSometime
(
2
, num
)
;
System
.out
.
println
(
"StringBuilder拼接 "
+
CYCLE_NUM_BIGGER
+
" 次,"
+ num
+
"次平均时间:"
+ time
+
" ms"
)
;
time
=
executeSometime
(
3
, num
)
;
System
.out
.
println
(
"String拼接单个字符串 "
+
CYCLE_NUM_LOWER
+
" 次,"
+ num
+
"次平均时间:"
+ time
+
" ms"
)
;
//java学习交流:737251827 进入可领取学习资源及对十年开发经验大佬提问,免费解答!
time
=
executeSometime
(
4
, num
)
;
System
.out
.
println
(
"StringBuilder拼接单个字符串 "
+
CYCLE_NUM_LOWER
+
" 次,"
+ num
+
"次平均时间:"
+ time
+
" ms"
)
;
}
}

结果分析
第一组
10_000_000 次循环拼接,在循环内使用 String 和 StringBuilder 的效率是一样的!为什么呢? 分享你看下。
使用
javap -c StringTest.class 反编译查看两个方法编译后的文件:
//java学习交流:737251827 进入可领取学习资源及对十年开发经验大佬提问,免费解答!
可以发现 String 方法拼接字符串编译器优化后使用的就是 StringBuilder、因此用例1 和用例2 的效率是一样的。
第二组
第二组的结果就是大家喜闻乐见的了,由于 10_000_000 次循环String 拼接实在太慢所以我采用了 10_000 次拼接来分析。
分析用例3:虽然编译器会对 String 拼接做优化,但是它每次在循环内创建 StringBuilder 对象,在循环内销毁。下次循环他有创建。相比较用例4在循环外创建,多了 n 次 new 对象、销毁对象的操作、n - 1 次将 StringBuilder 转换成 String 的操作 。效率低也是理所应当了。
扩展
/**
* 循环内 使用 StringBuilder 拼接字符串,一次循环后销毁
*/
public
static
void
useStringBuilderOut
(
)
{
StringBuilder sb
=
new
StringBuilder
(
)
;
for
(int i
=
0
; i
<
CYCLE_NUM_BIGGER
; i
++
)
{
// sb.setLength(0);
sb
.
delete
(
0
, sb
.
length
(
)
)
;
String s
= sb
.
append
(str1
)
.
append
(i
)
.
append
(str2
)
.
append
(i
)
.
append
(str3
)
.
append
(i
)
.
append
(str4
)
.
toString
(
)
;
}
}
public
static
void
createStringBuider
(
)
{
for
(int i
=
0
; i
<
CYCLE_NUM_BIGGER
; i
++
)
{
StringBuilder sb
=
new
StringBuilder
(
)
;
}
}
public
static
void
cleanStringBuider
(
)
{
StringBuilder sb
=
new
StringBuilder
(
)
;
for
(int i
=
0
; i
<
CYCLE_NUM_BIGGER
; i
++
)
{
sb
.
delete
(
0
, sb
.
length
(
)
)
;
}
}
结论
-
编译器会将 String 拼接优化成使用 StringBuilder,但是还是有一些缺陷的。主要体现在循环内使用字符串拼接,编译器不会创建单个 StringBuilder 以复用
-
对于多次循环内拼接一个字符串的需求:StringBuilder 很快,因为其避免了 n 次 new 对象、销毁对象的操作,n - 1 次将 StringBuilder 转换成 String 的操作
-
StringBuilder 拼接不适用于循环内每次拼接即用的操作方式。因为编译器优化后的 String 拼接也是使用 StringBuilder 两者的效率一样。后者写起来还方便...