大家对StringBuffer的第一个印象就是,用stringbuffer.append(xxxx)替换string+=xxx会更高效。
String str = "";
str += "xxx";//new多了一个String对象
StringBuffer sbf = new StringBuffer();
sbf.append("xxx");//原来的StringBuffer对象
OK,我们接下来看一下append的实现,该方法由StringBuffer的父类AbstractStringBuilder实现。
public AbstractStringBuilder append(String str) {
if (str == null) str = "null";
int len = str.length();
ensureCapacityInternal(count + len);//关键1
str.getChars(0, len, value, count);//关键2
count += len;
return this;
}
我们看到append代码最关键的其实只是两句话,分别是上面的关键1
/**
* This method has the same contract as ensureCapacity, but is
* never synchronized.
*/
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0)
expandCapacity(minimumCapacity);
}
/**
* This implements the expansion semantics of ensureCapacity with no
* size check or synchronization.
*/
void expandCapacity(int minimumCapacity) {http://product.suning.com/106010784.html#unknown
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);
}
关键1的代码很多,但是我们可以看到,其实很简单,就是value作为一个char数组,然后每次有新的内容加入就检测char数组value的长度是否足够,不够的话就按照一定的策略进行扩充(具体的可以参考ArrayList和HashMap的代码)。
由这里,我们可以知道,因为每次append都是追加到value这个数组,而不是重新初始化对象,所以,和+=string这种形式的通过new String的追加是不同的。从StringBuffer这个名字,我们也可以看出,是采用了缓冲的思想,但是我觉得它缓冲的不够彻底,因为时不时总是要重新调整数组生成。
在这里,我也实现了一个CharArrayBuffer的类,用来作为缓冲。
/**
* Created by lsz on 2014/8/12.
*/
public class CharArrayBuffer {
private static final int BUFSIZE = 1024*1024*2;//这里具体的大小看业务场景
private char[] buf = new char[BUFSIZE];
private int count = 0;
public void append(char c){
buf[count] = c;
count++;
}
public String toString(){
return new String(buf,0,count);
}
public void clean(){
count = 0;
}
}
看到这个实现,这个类比StringBuffer少了一个要不断扩充的value数组的机制,但是更坑爹的两个问题,一个是BUFSIZE是固定的,如果越出怎么办,另外不停得new,并不是所有的buf都要那么大的,是不是很浪费。这里,我要解释下,其中BUFSIZE固定是因为上面说了,要根据业务内容来评估大小,比如保持一张网页的内容,肯定足够的。另外,空间浪费的问题,就是今天的重点,采用池化的思想来管理这些对象。
池化是一个很经典的,用空间来换时间的思想(减少浪费和不停初始化的时间)。其中,连接池、对象池、缓冲池都是具体的表现。接下来我会实现一个简单的池,采用栈实现。
import sun.org.mozilla.javascript.internal.Synchronizer;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
/**
* Created by lsz on 2014/8/12.
*/
public class BufferPool {
private static Long lock = 0l;
private static BlockingDeque<CharArrayBuffer> bDeque = null;
public static CharArrayBuffer get(){
if(bDeque == null) {
synchronized(lock){
if(bDeque == null){
bDeque = new LinkedBlockingDeque<CharArrayBuffer>(50);
for(int i = 0;i<50;i++){
try {
bDeque.putFirst(new CharArrayBuffer());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
try {
return bDeque.takeFirst();
} catch (InterruptedException e) {
e.printStackTrace();
return null;
}
}
public static void returnPool(CharArrayBuffer buf){
try {
buf.clean();
bDeque.putFirst(buf);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
简简单单写的,可能有什么遗漏。。。实现了最简单的“池”。通过get和returnPool就可以得到和返回池里面继续待用。