String StringBuilder StringBuffer 对比

  我们都知道在一个循环内拼接字符串不能直接用+,而是要用StringBuilder或者StringBuffer代替。
  但是在循环内拼接字符串影响会有多大呢,会对程序产生多大影响呢,是0.1s还是1s,还是… ,用数据来说话。

  还有一个问题就是,循环内每次要输出一句话,用String+还是用StringBuilder好呢。
  顺便也测一下。

动手

  下面是代码和测试结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/**
* @version V1.0
*/
public class StringStringBuilderStringBufferTest {

public static void main(String[] args) {

int count = 100000;
int iteatorCount = 10;
boolean outFlag = false; // 测试输出字符串对效率的影响
StringStringBuilderStringBufferTest test = new StringStringBuilderStringBufferTest();

// 用第一个for循环抵消创建int的影响
for (int i = 0; i < count; i++) {

}

/**
* 测试一个循环内拼接字符串的影响
*/
Double[][] array = new Double[iteatorCount][3];
for (int i = 0; i < iteatorCount; i++) {
test.effect(count, array, i);
}

// 打印多次测试结果
for (int i = 0; i < iteatorCount; i++) { // 测试多少次
StringBuilder sb = new StringBuilder();
for (int j = 0; j < 3; j++) {
sb.append(array[i][j]);
sb.append(" ");
}
System.out.println(sb.toString()); // 一行是一次测试结果
}

/**
* 测试一个循环内,每次拼接一个字符串的影响
*/
// 多次测试,比较结果,避免随机对测试结果的影响
Double[][] array2 = new Double[iteatorCount][3];
for (int i = 0; i < iteatorCount; i++) {
test.effect2(count, outFlag, array2, i);
}

// 打印多次测试结果
for (int i = 0; i < iteatorCount; i++) { // 测试多少次
StringBuilder sb = new StringBuilder();
for (int j = 0; j < 3; j++) {
sb.append(array2[i][j]);
sb.append(" ");
}
System.out.println(sb.toString()); // 一行是一次测试结果
}

}

/**
*
* @param count
* 循环次数
* @param array
* 结果数据
* @param iteatorCount
* 第几次测试
*/
private void effect(int count, Double[][] array, int iteatorCount) {
/**
* 测试String
*/
long stringStart = System.currentTimeMillis();
String str = null;
for (int i = 0; i < count; i++) {
str += i;
}
long stringEnd = System.currentTimeMillis();

/**
* 测试StringBuilder
*/
long stringBuildStrat = System.currentTimeMillis();
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < count; i++) {
stringBuilder.append(i);
}
String str2 = stringBuilder.toString();
long stringBuildEnd = System.currentTimeMillis();

/**
* 测试StringBuffer
*/
long stringBufferStrat = System.currentTimeMillis();
StringBuffer stringBuffer = new StringBuffer();
for (int i = 0; i < count; i++) {
stringBuffer.append(i);
}
stringBuffer.toString();
long stringBufferEnd = System.currentTimeMillis();

array[iteatorCount][0] = 1.0 * (stringEnd - stringStart) / 1000;
array[iteatorCount][1] = 1.0 * (stringBuildEnd - stringBuildStrat) / 1000;
array[iteatorCount][2] = 1.0 * (stringBufferEnd - stringBufferStrat) / 1000;
}

/**
* @param count
* 循环次数
* @param outFlag
* 是否输出字符串
* @param array
* 结果数据
* @param iteatorCount
* 第几次测试
*/
private void effect2(int count, boolean outFlag, Double[][] array, int iteatorCount) {

/**
* 测试String
*/
long stringStart = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
String str = "i:" + i;
if (outFlag) {
System.out.println(str);
}
}
long stringEnd = System.currentTimeMillis();

/**
* 测试StringBuilder
*/
long stringBuildStrat = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
String str = new StringBuilder().append("i:").append(i).toString();
if (outFlag) {
System.out.println(str);
}
}
long stringBuildEnd = System.currentTimeMillis();

/**
* 测试StringBuffer
*/
long stringBufferStrat = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
String str = new StringBuffer().append("i:").append(i).toString();
if (outFlag) {
System.out.println(str);
}
}
long stringBufferEnd = System.currentTimeMillis();

array[iteatorCount][0] = 1.0 * (stringEnd - stringStart) / 1000;
array[iteatorCount][1] = 1.0 * (stringBuildEnd - stringBuildStrat) / 1000;
array[iteatorCount][2] = 1.0 * (stringBufferEnd - stringBufferStrat) / 1000;

}

}

effect方法里拼接1000000次字符串打印出的结果,
String StringBuilder StringBuffer
16.887 0.004 0.004
11.244 0.002 0.002
8.889 0.002 0.002
9.098 0.003 0.003
9.134 0.002 0.003
9.027 0.002 0.003
8.996 0.002 0.002
9.081 0.002 0.003
8.985 0.003 0.002
9.016 0.003 0.003

可以看到,String和StringBuilder的差别很大,StringBuilder和StringBuffer效率差不多,总体还是StringBuilder好一点。

effect2方法打印出的结果,
String StringBuilder StringBuffer
0.095 0.07 0.072
0.063 0.038 0.032
0.035 0.036 0.039
0.04 0.04 0.028
0.028 0.03 0.031
0.03 0.03 0.037
0.042 0.038 0.046
0.046 0.047 0.05
0.033 0.028 0.038
0.028 0.041 0.044

effect2结果看着不明显,再测一次
String StringBuilder StringBuffer
0.12 0.071 0.068
0.033 0.056 0.035
0.036 0.045 0.05
0.04 0.034 0.031
0.034 0.034 0.031
0.037 0.028 0.041
0.042 0.042 0.049
0.043 0.031 0.032
0.031 0.031 0.032
0.035 0.029 0.036

其实每次输出一个字符串的话,这几个都是可以的嘛,String+用起来比较方便
以后可以放心的用System.out.println(“第” + count + “行:” + line);了

搬运点东西

String StringBuilder StringBuffer 对比

String 字符串常量
StringBuffer 字符串变量(线程安全)
StringBuilder 字符串变量(非线程安全)
  简要的说, String 类型和 StringBuffer 类型的主要性能区别其实在于 String 是不可变的对象, 因此在每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,然后将指针指向新的 String 对象,所以经常改变内容的字符串最好不要用 String ,因为每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了以后, JVM 的 GC 就会开始工作,那速度是一定会相当慢的。
  而如果是使用 StringBuffer 类则结果就不一样了,每次结果都会对 StringBuffer 对象本身进行操作,而不是生成新的对象,再改变对象引用。所以在一般情况下我们推荐使用 StringBuffer ,特别是字符串对象经常改变的情况下。而在某些特别情况下, String 对象的字符串拼接其实是被 JVM 解释成了 StringBuffer 对象的拼接,所以这些时候 String 对象的速度并不会比 StringBuffer 对象慢,而特别是以下的字符串对象生成中, String 效率是远要比 StringBuffer 快的:
 String S1 = “This is only a” + “ simple” + “ test”;
 StringBuffer Sb = new StringBuilder(“This is only a”).append(“ simple”).append(“ test”);
 你会很惊讶的发现,生成 String S1 对象的速度简直太快了,而这个时候 StringBuffer 居然速度上根本一点都不占优势。其实这是 JVM 的一个把戏,在 JVM 眼里,这个
 String S1 = “This is only a” + “ simple” + “test”; 其实就是:
 String S1 = “This is only a simple test”; 所以当然不需要太多的时间了。但大家这里要注意的是,如果你的字符串是来自另外的 String 对象的话,速度就没那么快了,譬如:
 String S2 = “This is only a”;
 String S3 = “ simple”;
 String S4 = “ test”;
 String S1 = S2 +S3 + S4;
这时候 JVM 会规规矩矩的按照原来的方式去做

在大部分情况下 StringBuffer > String
StringBuffer
Java.lang.StringBuffer线程安全的可变字符序列。一个类似于 String 的字符串缓冲区,但不能修改。虽然在任意时间点上它都包含某种特定的字符序列,但通过某些方法调用可以改变该序列的长度和内容。
可将字符串缓冲区安全地用于多个线程。可以在必要时对这些方法进行同步,因此任意特定实例上的所有操作就好像是以串行顺序发生的,该顺序与所涉及的每个线程进行的方法调用顺序一致。
StringBuffer 上的主要操作是 append 和 insert 方法,可重载这些方法,以接受任意类型的数据。每个方法都能有效地将给定的数据转换成字符串,然后将该字符串的字符追加或插入到字符串缓冲区中。append 方法始终将这些字符添加到缓冲区的末端;而 insert 方法则在指定的点添加字符。
例如,如果 z 引用一个当前内容是“start”的字符串缓冲区对象,则此方法调用 z.append(“le”) 会使字符串缓冲区包含“startle”,而 z.insert(4, “le”) 将更改字符串缓冲区,使之包含“starlet”。
在大部分情况下 StringBuilder > StringBuffer
java.lang.StringBuilde
java.lang.StringBuilder一个可变的字符序列是5.0新增的。此类提供一个与 StringBuffer 兼容的 API,但不保证同步。该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)。如果可能,建议优先采用该类,因为在大多数实现中,它比 StringBuffer 要快。两者的方法基本相同。