StringBuffer与StringBuilder是java.lang包下被大家熟知的两个类。其异同为:一、长度都是可扩充的;二、StringBuffer是线程安全的,StringBuilder是线程不安全的。那么他们的长度是如何实现动态扩充以及StringBuffer的线程安全是如何实现的呢?通过“深度”阅读它们的源代码,最终弄明白其中的缘由。
首先上一张StringBuffer和StringBuilder类结构图:
抽象类AbstractStringBuilder(也是核心实现类)实现了Appendable和CharSequence两个接口;StringBuffer与StringBuilder统统继承自AbstractStringBuilder,并且实现了java.io.Serializable和CharSequence接口。
下面简单描述下这几个接口所起到的作用(引用自中文api)。
eg.StringBuffer中replace()方法
1
2
3
4
|
public
synchronized
StringBuffer replace(
int
start,
int
end, String str) {
super
.replace(start, end, str);
return
this
;
}
|
StringBuilder中replace():
1
2
3
4
|
public
StringBuilder replace(
int
start,
int
end, String str) {
super
.replace(start, end, str);
return
this
;
}
|
区别仅仅在方法签名上是否有synchronized。
另外需要稍稍注意的问题是:StringBuffer同步只同步目标,比如:sb.append("i am not synchronized"),sb是同步的,而其中的参数未必是同步的。
而它们两个可扩展长度则是通过ensureCapacity(int minimumCapacity)来验证当前长度是否小于参数minimumCapacity,如果成立则进行分配空间。分配新空间的步长为(当前长度+1)的两倍。
实现如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
public
void
ensureCapacity(
int
minimumCapacity) {
if
(minimumCapacity > value.length) {
expandCapacity(minimumCapacity);
}
}
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);
}
|
如果新的长度小于0(溢出了),则使用Integer的最大值作为长度。
另外,在阅读源码的过程中,发现两个有趣的问题,下面一一道来。
第一个,就是reverse的实现。其实我是第一次看StringBuilder和StringBuffer的源码,这里面的reverse的实现是我所知道的java中的最高效的实现,没有之一。
上源码,再做解释:
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
|
public
AbstractStringBuilder reverse() {
boolean
hasSurrogate =
false
;
int
n = count -
1
;
for
(
int
j = (n-
1
) >>
1
; j >=
0
; --j) {
char
temp = value[j];
char
temp2 = value[n - j];
if
(!hasSurrogate) {
hasSurrogate = (temp >= Character.MIN_SURROGATE && temp <= Character.MAX_SURROGATE)
|| (temp2 >= Character.MIN_SURROGATE && temp2 <= Character.MAX_SURROGATE);
}
value[j] = temp2;
value[n - j] = temp;
}
if
(hasSurrogate) {
// Reverse back all valid surrogate pairs
for
(
int
i =
0
; i < count -
1
; i++) {
char
c2 = value[i];
if
(Character.isLowSurrogate(c2)) {
char
c1 = value[i +
1
];
if
(Character.isHighSurrogate(c1)) {
value[i++] = c1;
value[i] = c2;
}
}
}
}
return
this
;
}
|
reverse分成两个部分:前面一个循环与后面的判断。
首先地一个循环很高效,循环次数为长度(count)的一半,而且使用>>位移运算,交换数组value[j]与value[n-j]的值。这里一是循环次数少,而是使用最高效的位移运算所以说这个reverse很高效。在反转过程中还完成了一件事:就是为hasSurrogate赋值。赋值的依据就是value[j]与value[n-j]两个字符时候有一个在\uD800和\uDFFF之间,如果有则赋值为true。
而hasSurrogate的值作为下面一个if分支的依据,如果为true,则从头到尾循环一遍。至于为何要判断hasSurrogate,以及下面一个循环的意义,其实到这里应该已经结束了,在我整理StringBuffer和StringBuilder结构图时发现(“刨祖坟”行家啊),发现它们两个又再次实现了CharSequence接口,为何说再次呢,因为AbstractStringBuilder已经实现了一次,不知何为!