String和StringBuffer

    |     2015年3月30日   |   Java 语法基础   |     0 条评论   |    1318

String 的值不可变,每次操作都会生成新对象,既慢又费内存。

StringBuffer 同样表示字符串,但内部实现不同:修改时尽量在原缓冲区上动手,不急着造新对象,内存上通常优于 String。

StringBuffer 默认分配 16 个字符的缓冲区,内容超出时自动扩容,而不是每次都换一个全新对象。它不像 String 那样支持字面量简写,只能 new:

StringBuffer str1 = new StringBuffer(); // 默认 16 个字符的缓冲区
StringBuffer str2 = new StringBuffer(512); // 容量 512
// 放入字符串,并在后面预留 16 个字符的空余
StringBuffer str3 = new StringBuffer("www.scxh.com");

一、StringBuffer 常用方法

方法偏重追加、插入、删除。实际开发里如果要对同一段内容频繁改动,优先用 StringBuffer(单线程可改用 StringBuilder)。

1)append()

append() 往当前内容末尾追加,调用后对象本身变了,不是换了一个引用:

StringBuffer str = new StringBuffer("biancheng100");
str.append(true); // 内容变成 biancheng100true

字符串的 + 在编译期会转成可变缓冲再 append(),最后 toString()。早期 javac 用的是 StringBuffer,较新的编译器多用 StringBuilder。循环里反复 str += fragment 仍会不断产生中间 String,效率远差于自己持有一个 Buffer。短字符串用 + 更直观,可以稍微牺牲效率换可读性。

2)deleteCharAt() / delete()

StringBuffer str = new StringBuffer("abcdef");
str.deleteCharAt(3); // 删掉索引 3 的 'd',剩下 abcef
str = new StringBuffer("abcdef");
str.delete(1, 4);    // 删 [1, 4),即 bcd,剩下 aef

3)insert()

在指定位置插入,可以看成 append 的升级版。索引 3 是 'd' 前面,所以插入后是 abcxyzdef(原文写成 abcdxyzef,位置算错了):

StringBuffer str = new StringBuffer("abcdef");
str.insert(3, "xyz"); // abcxyzdef

4)setCharAt()

StringBuffer str = new StringBuffer("abcdef");
str.setCharAt(3, 'z'); // abczef

以上只是常用方法,完整列表看 API 文档。

二、效率对比

把 26 个英文字母拼接 10000 次,差异会非常明显:

public class Demo {
    public static void main(String[] args) {
        String fragment = "abcdefghijklmnopqrstuvwxyz";
        int times = 10000;

        long timeStart1 = System.currentTimeMillis();
        String str1 = "";
        for (int i = 0; i < times; i++) {
            str1 += fragment;
        }
        long timeEnd1 = System.currentTimeMillis();
        System.out.println("String: " + (timeEnd1 - timeStart1) + "ms");

        long timeStart2 = System.currentTimeMillis();
        StringBuffer str2 = new StringBuffer();
        for (int i = 0; i < times; i++) {
            str2.append(fragment);
        }
        long timeEnd2 = System.currentTimeMillis();
        System.out.println("StringBuffer: " + (timeEnd2 - timeStart2) + "ms");
    }
}
// 原文机器上的一次结果:
// String: 5287ms
// StringBuffer: 3ms

叠加 30000 次时,原文测到 String 约 35923ms、StringBuffer 约 8ms。次数越多,差距越大。大量拼接请用 Buffer / Builder。

三、StringBuilder 与选型

StringBuilder 和 StringBuffer 功能、方法几乎一样:Buffer 线程安全,Builder 非线程安全,单线程时 Builder 略快。三者都实现了 CharSequence(length()、charAt、subSequence)。String 直接实现该接口;Builder 和 Buffer 都是可变序列,继承 AbstractStringBuilder。

类型 可变 线程安全 适用
String 否 是(不可变本身安全) 少量数据、字面量
StringBuilder 是 否 单线程大量拼接(通常最快)
StringBuffer 是 是 多线程大量拼接

速度大致是 StringBuilder > StringBuffer > String,这是相对值,不是绝对定律。

一句话总结:少量用 String;单线程大量改用 StringBuilder;多线程大量改用 StringBuffer。

转载请注明来源:String和StringBuffer
本文链接地址:https://ai.zhousir.top/?p=56
回复 取消