采用池化的思想替换StringBuffer的使用

本文探讨了StringBuffer类的高效实现原理,通过分析其关键代码,揭示了StringBuffer如何利用缓冲思想优化字符串拼接操作。进一步介绍了自定义CharArrayBuffer类及缓冲池技术的应用,旨在减少内存分配开销并提高性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    大家对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就可以得到和返回池里面继续待用。


### StringBuffer使用场景及示例 #### 1. 使用场景 StringBufferJava 中的一个可变字符序列类,其特点是线程安全。因此,在以下场景中适合使用 StringBuffer: - **多线程环境下的字符串操作**:由于 StringBuffer 的方法大多使用了 `synchronized` 关键字进行同步,因此在多线程环境中,当多个线程需要同时对同一个字符串进行修改时,可以确保线程安全[^4]。 - **频繁修改字符串内容的场合**:与不可变的 String 类不同,StringBuffer 是可变的,因此在需要频繁拼接、插入或删除字符串内容的情况下,使用 StringBuffer 可以避免创建大量新的字符串对象,从而提高性能[^5]。 然而,需要注意的是,由于 StringBuffer 的线程安全性带来了额外的性能开销,因此在单线程环境下,如果不需要线程安全,通常推荐使用 StringBuilder 以获得更高的性能[^4]。 --- #### 2. 示例代码 以下是一个简单的 StringBuffer 示例,展示了如何使用其常见方法进行字符串操作: ```java public class StringBufferExample { public static void main(String[] args) { // 创建一个初始值为 "Hello" 的 StringBuffer 对象 StringBuffer sb = new StringBuffer("Hello"); // 追加字符串 " World" sb.append(" World"); // 在索引位置 5 插入字符串 " Java" sb.insert(5, " Java"); // 删除索引范围 [5, 10) 的字符 sb.delete(5, 10); // 将 StringBuffer 转换为字符串并输出 System.out.println(sb.toString()); } } ``` 运行上述代码后,输出结果为: ``` Hello World ``` --- #### 3. 常用方法 以下是 StringBuffer 提供的一些常用方法及其功能说明: - **`append(CharSequence s)`**:将指定的字符序列追加到当前字符串缓冲区末尾。 - **`insert(int offset, CharSequence s)`**:在指定位置插入指定的字符序列。 - **`delete(int start, int end)`**:删除从 `start` 到 `end - 1` 的字符。 - **`reverse()`**:反转当前字符串缓冲区中的字符[^4]。 - **`toString()`**:将 StringBuffer 转换为普通字符串[^4]。 这些方法使得 StringBuffer 成为处理复杂字符串操作的强大工具。 --- #### 4. 注意事项 - **性能问题**:尽管 StringBuffer 是线程安全的,但其同步机制会导致性能下降。因此,在单线程环境下,建议使用非线程安全的 StringBuilder 来替代 StringBuffer。 - **选择建议**:根据实际需求选择合适的类。如果需要线程安全且涉及大量字符串操作,则使用 StringBuffer;如果仅需高效地处理字符串且不考虑线程安全,则使用 StringBuilder[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值