JDK源码分析之java.lang.StringBuilder,java.lang.StringBuffer

一名话总结:java.lang.StringBuilder 与 java.lang.StringBuffer 同是继承于 java.lang.AbstractStringBuilder,具体在功能实现大多在 AbstractStringBuilder 中,StringBuilder 和 StringBuffer 相当于对其进行的一个接口封装,区别只是一个作了同步封装、一个作非同步封装。

由表及里,首先从 StringBuilder 和 StringBuffer 源代码中的构造方法和 append,delete,replace,insert,toString 等方法研究起。

java.lang.StringBuilder

StringBuilder 是一个 final 类,不能被继承。其类继承父类和实现的接口关系如下所示:

1
2
3
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence{}

其内部代码中显式声明(不包括继承等隐式属性)的只有一个属性:serialVersionUID(序列化ID)。其构造方法的内部实现也是通过 super 方法调用父类构造方法实现,具体如下所示:

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
/**
* Constructs a string builder with no characters in it and an
* initial capacity of 16 characters.
*/
public StringBuilder() {
super(16);
}
/**
* @param capacity the initial capacity.
* @throws NegativeArraySizeException if the <code>capacity</code>
* argument is less than <code>0</code>.
*/
public StringBuilder(int capacity) {
super(capacity);
}
/**
* @param str the initial contents of the buffer.
* @throws NullPointerException if <code>str</code> is <code>null</code>
*/
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}
/**
* @param seq the sequence to copy.
* @throws NullPointerException if <code>seq</code> is <code>null</code>
*/
public StringBuilder(CharSequence seq) {
this(seq.length() + 16);
append(seq);
}

append 方法

仅以一个 append 方法为例具体看看其内部实现,代码如下:

1
2
3
4
public StringBuilder append(String str) {
super.append(str);
return this;
}

在该方法内部仍然是一个 super 方法,调用父类在方法实现,只是做了一层外壳。其它的 delete,replace,insert 方法源代码也是如此,这里就不一一展示了。相关的 append 重载方法源码如下所示:

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
public StringBuilder append(Object obj) {
return append(String.valueOf(obj));
}
public StringBuilder append(String str) {
super.append(str);
return this;
}
// Appends the specified string builder to this sequence.
private StringBuilder append(StringBuilder sb) {
if (sb == null)
return append("null");
int len = sb.length();
int newcount = count + len;
if (newcount > value.length)
expandCapacity(newcount);
sb.getChars(0, len, value, count);
count = newcount;
return this;
}
/**
* @param sb the <tt>StringBuffer</tt> to append.
* @return a reference to this object.
*/
public StringBuilder append(StringBuffer sb) {
super.append(sb);
return this;
}
/**
*/
public StringBuilder append(CharSequence s) {
if (s == null)
s = "null";
if (s instanceof String)
return this.append((String)s);
if (s instanceof StringBuffer)
return this.append((StringBuffer)s);
if (s instanceof StringBuilder)
return this.append((StringBuilder)s);
return this.append(s, 0, s.length());
}
/**
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public StringBuilder append(CharSequence s, int start, int end) {
super.append(s, start, end);
return this;
}
public StringBuilder append(char[] str) {
super.append(str);
return this;
}
/**
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public StringBuilder append(char[] str, int offset, int len) {
super.append(str, offset, len);
return this;
}
public StringBuilder append(boolean b) {
super.append(b);
return this;
}
public StringBuilder append(char c) {
super.append(c);
return this;
}
public StringBuilder append(int i) {
super.append(i);
return this;
}
public StringBuilder append(long lng) {
super.append(lng);
return this;
}
public StringBuilder append(float f) {
super.append(f);
return this;
}
public StringBuilder append(double d) {
super.append(d);
return this;
}

toString 方法

与 append,delete,replace,insert等方法不同的是,toString 方法不是通过 super 方法调用父类的实现。但其实现中所用到的 value,count 属性依然是从父类中继承的,其实现仍然很简单,如下所示:

1
2
3
4
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}

java.lang.StringBuffer

当认识了 java.lang.StringBuilder 后,再来学习 StringBuffer 就相当简单了。其类声明和构造方法与 StringBuilder 完全一样。各功能方法内部实现上也完全一样,具体实现调用 super 方法通过父类实现。唯一的不同之处便是:功能方法前面多了一个同步关键字 synchronized。这里只简单给出其部分源代码,以供参考。

类声明和构造方法源码如下:

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
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence {
/** use serialVersionUID from JDK 1.0.2 for interoperability */
static final long serialVersionUID = 3388685877147921107L;
/**
* Constructs a string buffer with no characters in it and an
* initial capacity of 16 characters.
*/
public StringBuffer() {
super(16);
}
/**
* Constructs a string buffer with no characters in it and
* the specified initial capacity.
*
* @param capacity the initial capacity.
* @exception NegativeArraySizeException if the <code>capacity</code>
* argument is less than <code>0</code>.
*/
public StringBuffer(int capacity) {
super(capacity);
}
/**
* @param str the initial contents of the buffer.
* @exception NullPointerException if <code>str</code> is <code>null</code>
*/
public StringBuffer(String str) {
super(str.length() + 16);
append(str);
}
/**
* @param seq the sequence to copy.
* @exception NullPointerException if <code>seq</code> is <code>null</code>
* @since 1.5
*/
public StringBuffer(CharSequence seq) {
this(seq.length() + 16);
append(seq);
}
}

append 功能方法源码如下:

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
public synchronized StringBuffer append(Object obj) {
super.append(String.valueOf(obj));
return this;
}
public synchronized StringBuffer append(String str) {
super.append(str);
return this;
}
/**
* @param sb the <tt>StringBuffer</tt> to append.
* @return a reference to this object.
* @since 1.4
*/
public synchronized StringBuffer append(StringBuffer sb) {
super.append(sb);
return this;
}
/**
* @param s the <code>CharSequence</code> to append.
* @return a reference to this object.
* @since 1.5
*/
public StringBuffer append(CharSequence s) {
// Note, synchronization achieved via other invocations
if (s == null)
s = "null";
if (s instanceof String)
return this.append((String)s);
if (s instanceof StringBuffer)
return this.append((StringBuffer)s);
return this.append(s, 0, s.length());
}
/**
* @throws IndexOutOfBoundsException {@inheritDoc}
* @since 1.5
*/
public synchronized StringBuffer append(CharSequence s, int start, int end)
{
super.append(s, start, end);
return this;
}
public synchronized StringBuffer append(char[] str) {
super.append(str);
return this;
}
/**
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public synchronized StringBuffer append(char[] str, int offset, int len) {
super.append(str, offset, len);
return this;
}
public synchronized StringBuffer append(boolean b) {
super.append(b);
return this;
}
public synchronized StringBuffer append(char c) {
super.append(c);
return this;
}
public synchronized StringBuffer append(int i) {
super.append(i);
return this;
}
/**
* @since 1.5
*/
public synchronized StringBuffer appendCodePoint(int codePoint) {
super.appendCodePoint(codePoint);
return this;
}
public synchronized StringBuffer append(long lng) {
super.append(lng);
return this;
}
public synchronized StringBuffer append(float f) {
super.append(f);
return this;
}
public synchronized StringBuffer append(double d) {
super.append(d);
return this;
}

java.lang.AbstractStringBuilder

StringBuilder,StringBuffer 均是继承于 AbstractStringBuilder ,而其方法具体实现均是调用父类的方法完成。则从功能实现上,AbstractStringBuilder 是核心。下面来研究其源码实现。

与 java.lang.String 类似,其底层仍是通过字符数组实现字符串的存储。不同的是多了一个 count 参数,以用于记录实际存储的字符个数,而不是字符数组 value 的长度。类声明、属性及构造方法源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
abstract class AbstractStringBuilder implements Appendable, CharSequence {
/**
* The value is used for character storage.
*/
char[] value;
/**
* The count is the number of characters used.
*/
int count;
/**
* This no-arg constructor is necessary for serialization of subclasses.
*/
AbstractStringBuilder() {
}
/**
* Creates an AbstractStringBuilder of the specified capacity.
*/
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
}

与 java.lang.String 相比,同是字符数组存储字符串,但 String 中声明的字符数组是 final 类型表示不可修改,而 AbstractStringBuilder 中则可以修改,这也就是为啥 StringBuilder、StringBuffer可实现字符串修改功能了。下面来看部分常用方法的具体实现。

append 方法

append 的重构方法比较多,但原理是类似的。功能都是将字符串、字符数组等添加到原字符串中,并返回新的字符串 AbstractStringBuilder。步骤如下:(1)对传入形参正确性进行检查;(2)对原字符数组长度进行检查,判断是否能容纳新加入的字符;(3)对原字符数组进行相应添加操作。

以形参为 String 在 append 方法源码为例。

1
2
3
4
5
6
7
8
public AbstractStringBuilder append(String str) {
if (str == null) str = "null";
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}

其中 ensureCapacityInternal 方法用于判断字符数组长度是否足够,如下所示:

1
2
3
4
5
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0)
expandCapacity(minimumCapacity);
}

当字符数组长度不够时,便创建一个新的数组,将原数组中数据拷贝到新数组中,具体拷贝方法由 Arrays.copyOf 方法实现,而 Arrays.copyOf 方法又是通过 System.arraycopy 来实现数组拷贝,该 System 方法为 native 方法。

新的数组长度取决于原数组长度和待添加的数组长度,如下所示:

1
2
3
4
5
6
7
8
9
10
11
void expandCapacity(int minimumCapacity) {
int newCapacity = value.length * 2 + 2;
if (newCapacity - minimumCapacity < 0)
newCapacity = minimumCapacity;
if (newCapacity < 0) {
if (minimumCapacity < 0) // overflow
throw new OutOfMemoryError();
newCapacity = Integer.MAX_VALUE;
}
value = Arrays.copyOf(value, newCapacity);
}

研究这段源码可以发现:如果可以提前预估出最终的数组长度并在创建对象时提前设置数组大小,对程序运行效率的提高是十分有帮助的。(减少了不断扩容、拷贝的内在及时间成本)

append 相当重载方法源码如下:

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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
/**
* @param obj an {@code Object}.
* @return a reference to this object.
*/
public AbstractStringBuilder append(Object obj) {
return append(String.valueOf(obj));
}
/**
* @param str a string.
* @return a reference to this object.
*/
public AbstractStringBuilder append(String str) {
if (str == null) str = "null";
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
// Documentation in subclasses because of synchro difference
public AbstractStringBuilder append(StringBuffer sb) {
if (sb == null)
return append("null");
int len = sb.length();
ensureCapacityInternal(count + len);
sb.getChars(0, len, value, count);
count += len;
return this;
}
// Documentation in subclasses because of synchro difference
public AbstractStringBuilder append(CharSequence s) {
if (s == null)
s = "null";
if (s instanceof String)
return this.append((String)s);
if (s instanceof StringBuffer)
return this.append((StringBuffer)s);
return this.append(s, 0, s.length());
}
/**
* @param s the sequence to append.
* @param start the starting index of the subsequence to be appended.
* @param end the end index of the subsequence to be appended.
* @return a reference to this object.
* @throws IndexOutOfBoundsException if
* {@code start} is negative, or
* {@code start} is greater than {@code end} or
* {@code end} is greater than {@code s.length()}
*/
public AbstractStringBuilder append(CharSequence s, int start, int end) {
if (s == null)
s = "null";
if ((start < 0) || (start > end) || (end > s.length()))
throw new IndexOutOfBoundsException(
"start " + start + ", end " + end + ", s.length() "
+ s.length());
int len = end - start;
ensureCapacityInternal(count + len);
for (int i = start, j = count; i < end; i++, j++)
value[j] = s.charAt(i);
count += len;
return this;
}
/**
* @param str the characters to be appended.
* @return a reference to this object.
*/
public AbstractStringBuilder append(char[] str) {
int len = str.length;
ensureCapacityInternal(count + len);
System.arraycopy(str, 0, value, count, len);
count += len;
return this;
}
/**
* @param str the characters to be appended.
* @param offset the index of the first {@code char} to append.
* @param len the number of {@code char}s to append.
* @return a reference to this object.
* @throws IndexOutOfBoundsException
* if {@code offset < 0} or {@code len < 0}
* or {@code offset+len > str.length}
*/
public AbstractStringBuilder append(char str[], int offset, int len) {
if (len > 0) // let arraycopy report AIOOBE for len < 0
ensureCapacityInternal(count + len);
System.arraycopy(str, offset, value, count, len);
count += len;
return this;
}
/**
* @param b a {@code boolean}.
* @return a reference to this object.
*/
public AbstractStringBuilder append(boolean b) {
if (b) {
ensureCapacityInternal(count + 4);
value[count++] = 't';
value[count++] = 'r';
value[count++] = 'u';
value[count++] = 'e';
} else {
ensureCapacityInternal(count + 5);
value[count++] = 'f';
value[count++] = 'a';
value[count++] = 'l';
value[count++] = 's';
value[count++] = 'e';
}
return this;
}
/**
* @param c a {@code char}.
* @return a reference to this object.
*/
public AbstractStringBuilder append(char c) {
ensureCapacityInternal(count + 1);
value[count++] = c;
return this;
}
/**
* @param i an {@code int}.
* @return a reference to this object.
*/
public AbstractStringBuilder append(int i) {
if (i == Integer.MIN_VALUE) {
append("-2147483648");
return this;
}
int appendedLength = (i < 0) ? Integer.stringSize(-i) + 1
: Integer.stringSize(i);
int spaceNeeded = count + appendedLength;
ensureCapacityInternal(spaceNeeded);
Integer.getChars(i, spaceNeeded, value);
count = spaceNeeded;
return this;
}
/**
* @param l a {@code long}.
* @return a reference to this object.
*/
public AbstractStringBuilder append(long l) {
if (l == Long.MIN_VALUE) {
append("-9223372036854775808");
return this;
}
int appendedLength = (l < 0) ? Long.stringSize(-l) + 1
: Long.stringSize(l);
int spaceNeeded = count + appendedLength;
ensureCapacityInternal(spaceNeeded);
Long.getChars(l, spaceNeeded, value);
count = spaceNeeded;
return this;
}
/**
* @param f a {@code float}.
* @return a reference to this object.
*/
public AbstractStringBuilder append(float f) {
new FloatingDecimal(f).appendTo(this);
return this;
}
/**
* @param d a {@code double}.
* @return a reference to this object.
*/
public AbstractStringBuilder append(double d) {
new FloatingDecimal(d).appendTo(this);
return this;
}

delete,replace,insert 方法

这三个方法的实现原理相似。

  • delete:可实现删除指定数组起始、终止位置之间的字符。将指定终止位置之后的字符依次向前移动 len 个字符,将起始位置的字符开始依次覆盖掉,相当于字符数组拷贝。
  • replace:字符数组拷贝。
  • insert:在数组指定位置插入字符,底层也是字符数组拷贝。

其源码如下:

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
/**
* @param start The beginning index, inclusive.
* @param end The ending index, exclusive.
* @return This object.
* @throws StringIndexOutOfBoundsException if {@code start}
* is negative, greater than {@code length()}, or
* greater than {@code end}.
*/
public AbstractStringBuilder delete(int start, int end) {
if (start < 0)
throw new StringIndexOutOfBoundsException(start);
if (end > count)
end = count;
if (start > end)
throw new StringIndexOutOfBoundsException();
int len = end - start;
if (len > 0) {
System.arraycopy(value, start+len, value, start, count-end);
count -= len;
}
return this;
}
/**
* @param start The beginning index, inclusive.
* @param end The ending index, exclusive.
* @param str String that will replace previous contents.
* @return This object.
* @throws StringIndexOutOfBoundsException if <code>start</code>
* is negative, greater than <code>length()</code>, or
* greater than <code>end</code>.
*/
public AbstractStringBuilder replace(int start, int end, String str) {
if (start < 0)
throw new StringIndexOutOfBoundsException(start);
if (start > count)
throw new StringIndexOutOfBoundsException("start > length()");
if (start > end)
throw new StringIndexOutOfBoundsException("start > end");
if (end > count)
end = count;
int len = str.length();
int newCount = count + len - (end - start);
ensureCapacityInternal(newCount);
System.arraycopy(value, end, value, start + len, count - end);
str.getChars(value, start);
count = newCount;
return this;
}
/**
* @param index position at which to insert subarray.
* @param str A {@code char} array.
* @param offset the index of the first {@code char} in subarray to
* be inserted.
* @param len the number of {@code char}s in the subarray to
* be inserted.
* @return This object
* @throws StringIndexOutOfBoundsException if {@code index}
* is negative or greater than {@code length()}, or
* {@code offset} or {@code len} are negative, or
* {@code (offset+len)} is greater than
* {@code str.length}.
*/
public AbstractStringBuilder insert(int index, char[] str, int offset,
int len)
{
if ((index < 0) || (index > length()))
throw new StringIndexOutOfBoundsException(index);
if ((offset < 0) || (len < 0) || (offset > str.length - len))
throw new StringIndexOutOfBoundsException(
"offset " + offset + ", len " + len + ", str.length "
+ str.length);
ensureCapacityInternal(count + len);
System.arraycopy(value, index, value, index + len, count - index);
System.arraycopy(str, offset, value, index, len);
count += len;
return this;
}

toString 方法

该方法是此抽象类中唯一一个抽象方法,功能就不多说了。

总结

java.lang.StringBuilder 和 java.lang.StringBuffer 只是对 java.lang.AbstractStringBuilder 的一个继承封装,通过继承可以实现功能的一个拓展。StringBuilder仅仅只是功能的继承;StirngBuffer在功能继承上做了一个synchronized加锁的操作,从而实现线程安全性。

AbstractStringBuilder 才是功能方法的具体实现。同 java.lang.String 一样,底层是用字符数组在存储字符串,但区别是 String 中字符数组是 final 类型,而 AbstractStringBuilder 中字符数组是可变的。

StringBuilder 与 StringBuffer 均是 final 类,无法再被继承。