JAVA菜鸟入门篇 - JAVA的StringBuilder类和StringBuffer类(二十四)

本文详细介绍了Java中String、StringBuilder和StringBuffer的区别与应用场景,通过代码示例对比了不同字符串操作方式的性能表现。


String、StringBuilder和StringBuffer


String对象是不可改变的。每次使用 System.String类中的方法之一时,都要在内存中创建一个新的字符串对象,这就需要为该新对象分配新的空间。在需要对字符串执行重复修改的情况下,与创建新的 String对象相关的系统开销可能会非常昂贵。如果要修改字符串而不创建新的对象,则可以使用System.Text.StringBuilder类。例如,当在一个循环中将许多字符串连接在一起时,使用 StringBuilder类可以提升性能。

  通过用一个重载的构造函数方法初始化变量,可以创建 StringBuilder类的新实例,正如以下示例中所阐释的那样。

StringBuilder MyStringBuilder = new StringBuilder("Hello World!");

(一)设置容量和长度
  虽然 StringBuilder对象是动态对象,允许扩充它所封装的字符串中字符的数量,但是您可以为它可容纳的最大字符数指定一个值。此值称为该对象的容量,不应将它与当前 StringBuilder对象容纳的字符串长度混淆在一起。例如,可以创建 StringBuilder类的带有字符串“Hello”(长度为 5)的一个新实例,同时可以指定该对象的最大容量为 25。当修改 StringBuilder时,在达到容量之前,它不会为其自己重新分配空间。当达到容量时,将自动分配新的空间且容量翻倍。(容量的扩展调用的是父类AbstractStringBuilder中的ensureCapacityInternal()方法和expandCapacity()方法)可以使用重载的构造函数之一来指定 StringBuilder类的容量。以下代码示例指定可以将 MyStringBuilder对象扩充到最大 25个空白。


StringBuilderMyStringBuilder = new StringBuilder("Hello World!", 25);
另外,可以使用读/写 Capacity属性来设置对象的最大长度。以下代码示例使用 Capacity属性来定义对象的最大长度。
MyStringBuilder.Capacity= 25;

(二)下面列出了此类的几个常用方法:


(1)Append() 方法可用来将文本或对象的字符串表示形式添加到由当前 StringBuilder对象表示的字符串的结尾处。以下示例将一个StringBuilder对象初始化为“Hello World”,然后将一些文本追加到该对象的结尾处。将根据需要自动分配空间。

StringBuilder strb = new StringBuilder("Hello World!");
strb.append(" What a beautiful day.");
System.out.println(strb);
此示例将 Hello World! What abeautiful day.显示到控制台。

(2)append()方法的重载可以看 StringBuilder类的API文档

(3)Insert 方法将字符串或对象添加到当前 StringBuilder中的指定位置。以下示例使用此方法将一个单词插入到 StringBuilder的第六个位置。
StringBuilder strb = new StringBuilder("Hello World!");
System.out.println(strb.insert(6,"Beautiful "));
此示例将 Hello BeautifulWorld!显示到控制台。

(4)使用 Replace方法,可以用另一个指定的字符来替换 StringBuilder对象内的字符。以下示例使用 Replace方法来搜索 StringBuilder对象,查找所有的感叹号字符 (!),并用问号字符 (?)来替换它们。
StringBuilder strb = new StringBuilder("Hello World!");
System.out.println(strb.replace('!', '?'));

注意:

StringBuffer中方法的返回语句是return this,这样很方便写所谓的方法链

StringBuilder strb = new StringBuilder();

System.out.println(strb.append("123").append("456").append("789"));

输出结果:123456


Java的StringBuilder类


     如果程序对附加字符串的需求很频繁,不建议使用+来进行字符串的串联。可以考虑使用java.lang.StringBuilder 类,使用这个类所产生的对象默认会有16个字符的长度,您也可以自行指定初始长度。如果附加的字符超出可容纳的长度,则StringBuilder 对象会自动增加长度以容纳被附加的字符。如果有频繁作字符串附加的需求,使用StringBuilder 类能使效率大大提高。如下代码:

Java代码 
public   class AppendStringTest   
{   
     public   static   void main(String[] args)   
            {   
                   String text = "" ;   
                  
                   long beginTime = System.currentTimeMillis();   
                   for ( int i= 0 ;i< 10000 ;i++)   
                         text = text + i;   
                   long endTime = System.currentTimeMillis();   
                   System.out.println("执行时间:" +(endTime-beginTime));   
   
                   StringBuilder sb = new StringBuilder ( "" );   
                   beginTime = System.currentTimeMillis();   
                   for ( int i= 0 ;i< 10000 ;i++)   
                          sb.append(String.valueOf(i));   
                   endTime = System.currentTimeMillis();   
                   System.out.println("执行时间:" +(endTime-beginTime));   
   
            }   
}   
public class AppendStringTest
{
     public static void main(String[] args)
            {
                   String text = "";
               
                   long beginTime = System.currentTimeMillis();
                   for(int i=0;i<10000;i++)
                         text = text + i;
                   long endTime = System.currentTimeMillis();
                   System.out.println("执行时间:"+(endTime-beginTime));

                   StringBuilder sb = new StringBuilder ("");
                   beginTime = System.currentTimeMillis();
                   for(int i=0;i<10000;i++)
                          sb.append(String.valueOf(i));
                   endTime = System.currentTimeMillis();
                   System.out.println("执行时间:"+(endTime-beginTime));

            }
} 此段代码输出:

执行时间:3188
执行时间:15


总结:


String 字符串常量 (不可变字符序列)
StringBuffer 字符串变量(线程安全,可变字符序列,效率低
StringBuilder 字符串变量(非线程安全,可变字符序列,效率高


简要的说:

String 类型和 StringBuffer 类型的主要性能区别其实在于 String 是不可变的对象, 因此在每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,然后将指针指向新的 String 对象,所以经常改变内容的字符串最好不要用 String ,因为每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了以后, JVM 的 GC 就会开始工作,那速度是一定会相当慢的。
  而如果是使用 StringBuffer 类则结果就不一样了,每次结果都会对 StringBuffer 对象本身进行操作,而不是生成新的对象,再改变对象引用。所以在一般情况下我们推荐使用 StringBuffer ,特别是字符串对象经常改变的情况下。而在某些特别情况下, String 对象的字符串拼接其实是被 JVM 解释成了 StringBuffer 对象的拼接,所以这些时候 String 对象的速度并不会比 StringBuffer 对象慢,而特别是以下的字符串对象生成中, String 效率是远要比 StringBuffer 快的:


 String S1 = “This is only a” + “ simple” + “ test”;
 StringBuffer Sb = new StringBuilder(“This is only a”).append(“ simple”).append(“ test”);
 你会很惊讶的发现,生成 String S1 对象的速度简直太快了,而这个时候 StringBuffer 居然速度上根本一点都不占优势。其实这是 JVM 的一个把戏,在 JVM 眼里,这个
 String S1 = “This is only a” + “ simple” + “test”; 其实就是:
 String S1 = “This is only a simple test”; 所以当然不需要太多的时间了。但大家这里要注意的是,如果你的字符串是来自另外的 String 对象的话,速度就没那么快了,譬如:
String S2 = “This is only a”;
String S3 = “ simple”;
String S4 = “ test”;
String S1 = S2 +S3 + S4;
这时候 JVM 会规规矩矩的按照原来的方式去做


在大部分情况下 StringBuffer > String

StringBuffer
Java.lang.StringBuffer线程安全的可变字符序列。一个类似于 String 的字符串缓冲区,但不能修改。虽然在任意时间点上它都包含某种特定的字符序列,但通过某些方法调用可以改变该序列的长度和内容。
可将字符串缓冲区安全地用于多个线程。可以在必要时对这些方法进行同步,因此任意特定实例上的所有操作就好像是以串行顺序发生的,该顺序与所涉及的每个线程进行的方法调用顺序一致。
StringBuffer 上的主要操作是 append 和 insert 方法,可重载这些方法,以接受任意类型的数据。每个方法都能有效地将给定的数据转换成字符串,然后将该字符串的字符追加或插入到字符串缓冲区中。append 方法始终将这些字符添加到缓冲区的末端;而 insert 方法则在指定的点添加字符。
例如,如果 z 引用一个当前内容是“start”的字符串缓冲区对象,则此方法调用 z.append("le") 会使字符串缓冲区包含“startle”,而 z.insert(4, "le") 将更改字符串缓冲区,使之包含“starlet”。

在大部分情况下 StringBuilder > StringBuffer

java.lang.StringBuilder
java.lang.StringBuilder一个可变的字符序列是5.0新增的。此类提供一个与 StringBuffer 兼容的 API,但不保证同步。该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)。如果可能,建议优先采用该类,因为在大多数实现中,它比 StringBuffer 要快。两者的方法基本相同。

本文参考:


### 区别与内部机制 Java 中的 `StringBuilder` `StringBuffer` 都是用于处理可变字符串的,它们的内部实现似于可变长度的字符数组,通过 `append()` 方法进行追加操作时,不会每次都创建新的对象,而是在内部数组上直接修改。这种机制显著提高了在频繁修改字符串内容时的性能[^1]。 两者的主要区别在于线程安全性: - **`StringBuffer`** 是线程安全的,它的方法都被 `synchronized` 修饰,因此可以在多线程环境中安全使用。 - **`StringBuilder`** 则不是线程安全的,它没有同步机制,因此在单线程环境下性能更优。 例如,通过 `hashCode()` 的输出可以验证,对 `StringBuffer` `StringBuilder` 的修改不会创建新对象: ```java public static void main(String[] args) { StringBuffer stringBuffer = new StringBuffer(); System.out.println("修改前stringBuffer的hashCode:" + stringBuffer.hashCode()); stringBuffer.append("b"); System.out.println("修改后stringBuffer的hashCode:" + stringBuffer.hashCode()); System.out.println("------------------------------------------------------"); StringBuilder stringBuilder = new StringBuilder(); System.out.println("修改前stringBuilder的hashCode:" + stringBuilder.hashCode()); stringBuilder.append("b"); System.out.println("修改后stringBuilder的hashCode:" + stringBuilder.hashCode()); } ``` 运行结果将显示,无论是 `StringBuffer` 还是 `StringBuilder`,其对象的 `hashCode` 在修改前后保持不变,说明对象本身未发生变化[^2]。 ### 性能对比 在单线程环境下,`StringBuilder` 的性能优于 `StringBuffer`。下面的代码演示了这一点: ```java long start = System.currentTimeMillis(); StringBuilder sb = new StringBuilder(); for (int i = 0; i < 1000000; i++) { sb.append(i); } System.out.println("StringBuilder耗时:" + (System.currentTimeMillis() - start) + "ms"); start = System.currentTimeMillis(); StringBuffer sbf = new StringBuffer(); for (int i = 0; i < 1000000; i++) { sbf.append(i); } System.out.println("StringBuffer耗时:" + (System.currentTimeMillis() - start) + "ms"); ``` 运行结果通常显示 `StringBuilder` 比 `StringBuffer` 快约 30%,因为 `StringBuffer` 的同步机制带来了额外的开销[^3]。 ### 使用场景 - **`StringBuilder`**:适用于单线程环境下的字符串拼接操作,如日志生成、字符串处理等场景,推荐在不需要线程安全的情况下使用。 - **`StringBuffer`**:适用于多线程环境下需要共享字符串缓冲区的场景,如多个线程同时修改同一个字符串内容时,确保线程安全。 ### 示例:CSV 文件生成器 一个实际应用场景是生成 CSV 文件内容,假设需要拼接大量数据行,使用 `StringBuilder` 可以高效完成: ```java public class CSVGenerator { public static String generateCSV(int rows) { StringBuilder csv = new StringBuilder(); for (int i = 0; i < rows; i++) { csv.append("Row").append(i).append(",Value").append(i).append("\n"); } return csv.toString(); } public static void main(String[] args) { System.out.println(generateCSV(5)); } } ``` 该示例使用 `StringBuilder` 构建 CSV 数据,性能优于使用 `String` 拼接的方式,同时也适用于大多数单线程场景[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值