StringBuffer与StringBuilder

!!! note 目录

StringBuffer与StringBuilder

  • StringBufferStringBuilder 都是 Java 中用于创建和操作可变字符串的类都继承同一父类AbstractStringBuilder
  • 底层是byte[]数组,并且这个byte[]数组没有被final修饰,所以当byte[]数组满了,可以创建一个更大的新数组来达到扩容,然后他可以重新指向这个新数组对象。
  • 优化策略:可以预估字符串的大致长度,初始化时设置合适的容量可以减少扩展数组的次数,从而提高性能。
  • 在 Java 中,StringBuffer 和 StringBuilder 的初始默认容量(即字符数组的初始大小)都是 16。这个默认容量是由它们的共同父类 AbstractStringBuilder 定义的。
  • StringBuilder和StringBuffer每次扩容为原来的2倍再加上2。

一、主要区别

  1. 线程安全性

    • StringBuffer 是线程安全的,它的方法被同步以确保线程安全。因此,它可以在多线程环境中安全地使用。
    • StringBuilder 不是线程安全的,它的性能比 StringBuffer 更好,但不适合在多线程环境中使用。
  2. 性能

    • 由于 StringBuffer 的同步机制,它的操作速度相对较慢。
    • StringBuilder 没有同步机制,因此在单线程环境中操作速度更快。
  3. 同步

    • StringBuffer:同步的,适合多线程环境。
    • StringBuilder:非同步的,适合单线程环境。

二、StringBuilder和StringBuffer构造方法

2.1 StringBuilder 构造方法

  1. 默认构造方法

    • 创建一个容量为 16 的空 StringBuilder
    1
    StringBuilder sb = new StringBuilder();
  2. 指定初始容量的构造方法

    • 创建一个指定初始容量的 StringBuilder
    1
    StringBuilder sb = new StringBuilder(int capacity);

    这里 capacity 是字符数组的初始容量。如果未指定,默认为 16。

  3. 从字符串构造

    • 使用现有的字符串来初始化 StringBuilder
    1
    StringBuilder sb = new StringBuilder(String str);

    这将创建一个与给定字符串相同内容的 StringBuilder

2.2 StringBuffer 构造方法

  1. 默认构造方法

    • 创建一个容量为 16 的空 StringBuffer
    1
    StringBuffer sb = new StringBuffer();
  2. 指定初始容量的构造方法

    • 创建一个指定初始容量的 StringBuffer
    1
    StringBuffer sb = new StringBuffer(int capacity);

    这里 capacity 是字符数组的初始容量。如果未指定,默认为 16。

  3. 从字符串构造

    • 使用现有的字符串来初始化 StringBuffer
    1
    StringBuffer sb = new StringBuffer(String str);

    这将创建一个与给定字符串相同内容的 StringBuffer

三、StringBuilder和StringBuffer的常用方法

  1. StringBuilder append(Type data)

append 方法可以接受不同类型的数据作为参数,并将这些数据附加到现有的字符串构建器对象中而不产生新的对象。

1
2
3
4
5
6
@Test
public void testAppend(){
StringBuilder builder = new StringBuilder();
builder.append("Camellia");
System.out.println(builder);
}
  1. StringBuilder delete(int start, int end)

StringBuilder 类中的 delete(int start, int end) 方法用于删除字符序列中的一部分。
该方法接受两个参数:start end,它们定义了要删除的子字符串的起始和结束位置。
删除操作包括从 start包含)到 end不包含)之间的所有字符。

1
2
3
4
5
6
@Test
public void testDelete(){
StringBuilder builder = new StringBuilder("Camellia.xiaohua");
builder.delete(8,16);
System.out.println(builder);
}
  1. StringBuilder deleteCharAt(int index)

StringBuilder 类中的 deleteCharAt(int index) 方法用于删除指定索引位置的字符。
这个方法会移除 StringBuilder 对象中指定位置的字符,使得字符序列在该位置的字符被移除,并且之后的字符会向前移动。

1
2
3
4
5
6
@Test
public void testDeleteCharAt(){
StringBuilder builder = new StringBuilder("Camellia.xiaohua");
builder.deleteCharAt(8);
System.out.println(builder);
}
  1. StringBuilder insert(int offset,String str)

StringBuilder 类中的insert(int offset, String str)方法用于在StringBuilder对象的指定位置插入一个字符串。
offset 参数确定了插入点的位置,str 是要插入的字符串。插入操作会将指定位置及其后的字符向后移动,给新的字符串腾出空间。

1
2
3
4
5
6
@Test
public void testInsert(){
StringBuilder builder = new StringBuilder("重剑无锋,");
builder.insert(5,"大巧无工。");
System.out.println(builder);
}
  1. StringBuilder replace(int start,int end,String str)

StringBuilder 类中的replace(int start, int end, String str)方法用于替换StringBuilder对象中指定位置的字符序列。
该方法会将从startend(不包括end)的字符替换为新提供的字符串 str

1
2
3
4
5
6
@Test
public void testReplace(){
StringBuilder builder = new StringBuilder("xiaohua");
builder.replace(0,7,"Camellia");
System.out.println(builder);
}
  1. StringBuilder reverse()

StringBuilder 类中的reverse()方法用于将字符串反转。它会改变StringBuilder对象中的字符顺序,使得字符串中的字符按相反的顺序排列。

1
2
3
4
5
@Test
public void testReverse(){
StringBuilder builder = new StringBuilder("Camellia");
System.out.println(builder.reverse());
}
  1. void setCharAt(int index,char ch)

在 Java 的StringBuilder类中,setCharAt(int index, char ch) 方法用于将指定索引位置的字符替换为新的字符。
这个方法是对内部字符数组的直接操作,因此它可以在StringBuilder对象的原始数据上进行修改,而不需要创建新的字符串对象。

1
2
3
4
5
6
@Test
public void testSetCharAt(){
StringBuilder builder = new StringBuilder("Cameliia");
builder.setCharAt(5,'l');
System.out.println(builder);
}
  1. void setLength(int newLength)

在 Java 的 StringBuilder 类中,setLength(int newLength) 方法用于设置字符串的长度。
这个方法可以用来增加或减少 StringBuilder 对象中字符的数量。当你使用 setLength 方法时,它会调整 StringBuilder 中实际存储的字符数组的大小,以符合指定的长度。
如果新长度小于当前长度,超出的部分将被截断;如果新长度大于当前长度,原字符串的末尾会填充空格字符(如果没有指定其他字符填充的话)。

1
2
3
4
5
6
7
@Test
public void testSetLength(){
StringBuilder builder = new StringBuilder("Camellia.xiaohua");
builder.setLength(8);
System.out.println(builder); // Camellia
}

注:

  • StringBuffer和StringBuilder方法基本相同,除非在涉及多线程的问题上。
  • 在多线程部分在具体学习StringBuffer
  • 还有一些方法和String基本相同。

四、性能分析(String、StringBuilder)

4.1 String

  1. 不可变性String 对象在创建后是不可变的。这意味着每当你对一个 String 对象进行拼接或修改操作时,实际上会创建一个新的 String 对象。这种特性导致了以下几个性能影响:

    • 内存使用:每次拼接都会生成新的 String 对象,这会导致大量的内存分配和垃圾回收。
    • 性能:频繁的字符串拼接操作会导致性能下降,因为每次拼接都会产生新的字符串实例,并且每次操作后都需要进行垃圾回收。
  2. 适用场景:适用于较少修改的字符串,或不需要频繁进行字符串拼接的场景。

4.2 StringBuilder

  1. 可变性StringBuilder 是可变的,这意味着你可以在同一个对象上多次修改字符串内容,而不需要创建新的对象。这有以下几个优势:

    • 内存使用:由于避免了不必要的对象创建,StringBuilder 在进行多次拼接时更加节省内存。
    • 性能StringBuilder 在进行字符串拼接时通常比 String 更高效,因为它直接在内部的字符数组上进行操作,而不是生成新的字符串对象。
  2. 适用场景:适用于频繁进行字符串拼接或修改的场景。

性能对比

  • 内存开销StringBuilder 由于不需要为每次拼接操作创建新对象,因此在频繁拼接的情况下,相较于 String 更加节省内存。
  • 性能:在进行大量字符串拼接时,StringBuilder 显示出比 String 更高的性能。因为它通过内部的字符数组直接操作,避免了每次操作后的对象创建和垃圾回收。
  • 线程安全性String 是线程安全的,因为它的不可变性保证了多个线程访问时不会发生状态变化。而 StringBuilder 由于是可变的,在多线程环境中使用时需要额外的同步措施。

4.3 代码测试

1
2
3
4
5
6
7
8
9
10
11
@Test
public void testStringPerformance(){
long begin=System.currentTimeMillis();
String s="";
for(int i=0;i<500000;i++){
s+=i;
}
long end=System.currentTimeMillis();
System.out.println("总耗时:"+(end-begin)+"毫秒");
}
//总耗时:80646毫秒,相当于80多秒。
1
2
3
4
5
6
7
8
9
10
11
 @Test
public void testStringBuilderPerformance(){
long begin=System.currentTimeMillis();
StringBuilder s = new StringBuilder();
for(int i=0;i<500000;i++){
s.append(i);
}
long end=System.currentTimeMillis();
System.out.println("总耗时:"+(end-begin)+"毫秒");
}
//总耗时:20毫秒

相差太多,根本不是一个量级的。推荐频繁拼串使用StringBuilder。