String,StringBuffer,StringBuilder拼接速度的测试

本文深入探讨了Java中String、StringBuilder与StringBuffer在字符串拼接操作上的性能差异,通过实验验证了StringBuilder的非线程安全性使其在大量拼接操作中表现最优,而StringBuffer虽线程安全但效率较低。

Java面试汇总中老是会有String,StringBuffer,StringBuilder的区别这种面试题,统一的回答是:

三者连接速度由快到慢:StringBuilder,StringBuffer,String

StringBuilder非线程安全,StringBuffer线程安全

String是字符串常量,二StringBuilder和StringBuffer为字符串变量,也就是说String一旦创建就不可更改,每次做连接操作时实际上又重新创建了一个String,然后将拼接后的结果存储在这个新的string对象指向的地址里,由于不断创建新的String对象导致了连接速度大大降低,而StringBuffer和StringBuilder则是直接在字符串变量后进行连接操作,速度才会明显快于String。

至于StringBuffer线程安全则是由于StringBuffer中的大部分方法都被synchronized(关于synchronized关键字本人之前的博客有介绍,感兴趣可以。。。嘿嘿嘿)修饰,简单来说就是实现了同步机制,导致了速度慢于StringBuilder

 那么真实的场景下到底结果如何呢?让我们来梭哈一段代码测试一下

public class StringBuliderBuffer {
	private static StringBuilder sbu=new StringBuilder("a");
	private static StringBuffer sbf=new StringBuffer("a");
	private static String str=new String("a");
	private static long count=1000;
	public static void main(String[] args) {
		
		long startSbuTime=System.currentTimeMillis();
		for (long i = 0; i < count; i++) {
			sbf.append(i);
		}
		long endSbuTime=System.currentTimeMillis();
		System.out.println("StringBuffer运行时间:"+(endSbuTime-startSbuTime));
		
		startSbuTime=System.currentTimeMillis();
		
		for (long i = 0; i < count; i++) {
			sbu.append(i);
		}
		endSbuTime=System.currentTimeMillis();		
		System.out.println("StringBuilder运行时间:"+(endSbuTime-startSbuTime));
		
		startSbuTime=System.currentTimeMillis();
		for (long i = 0; i < count; i++) {
			str=str+i;
		}
		endSbuTime=System.currentTimeMillis();	
		System.out.println("String运行时间:"+(endSbuTime-startSbuTime));
	}
}

分别创建了各自的对象,并且初始化,然后测试执行循环连接,测试连接前后的时间差,结果如下

次数count=1000时

StringBuffer运行时间:1
StringBuilder运行时间:0
String运行时间:3

次数count=100000

StringBuffer运行时间:16
StringBuilder运行时间:0
String运行时间:22418

次数count=1000000

StringBuffer运行时间:78
StringBuilder运行时间:31

次数为1000次时,结果不明显

十万次时结果已经比较明显,StringBuilder的时间仍未0(这个有点诡异),而String时间明显与其它俩天壤之别

一百万次时StringBuilder和StringBuffer差距拉开,总得来说仍在同一个等级上,而String结果仍未完成(此时已等待了10分钟,我洗好了衣服回来仍未结束)

所以应尽少使用String的拼接操作,但是在内存中已经有拼接后的字符串这种情况时,String的速度快于其它两者,可能因为拼接后的字符串如果已经存在,String不会再做拼接操作,而是将当前字符串指向已有字符串的地址,所以速度很快

至于StringBuffer和StringBuilder的选择,由于时间差不多,所以一般选择线程安全的StringBuffer

 

<think>嗯,用户之前问过关于String类能否被继承的问题,现在又接着问StringStringBufferStringBuilder的区别。看来他们可能是在学习Java字符串处理相关的知识,或者是在准备面试题。我需要详细分析这三个类的区别,确保回答全面且易懂。 首先,要明确这三个类的用途和设计目的。String是不可变的,而StringBufferStringBuilder是可变的。但用户可能需要知道为什么会有这样的设计,以及在实际应用中如何选择。 然后,线程安全是一个重点。StringBuffer是线程安全的,因为它的方法都是同步的,而StringBuilder不是。需要解释清楚线程安全的概念,以及在不同场景下的影响。 性能比较也很重要。通常StringBuilder在单线程下更快,但为什么?需要提到同步带来的开销,以及字符串操作的频繁程度如何影响性能。 接下来,可能需要举例说明它们的用法差异。比如字符拼接时,使用String会生成多个对象,而使用StringBuilderStringBuffer可以避免这种情况,提高效率。 用户可能还会关心什么时候应该用哪个类。比如在单线程环境下推荐使用StringBuilder,多线程则用StringBuffer,而String适用于不需要修改字符串内容的情况。 此外,需要提到JVM对String的优化,比如字符串常量池,以及如何通过final关键字保证不可变性。还有,StringBufferStringBuilder的API几乎相同,但同步处理不同。 要避免过于技术化的术语,用简单易懂的语言解释。可能还要对比它们的继承结构,比如都继承自AbstractStringBuilder,但String是独立的。 最后,总结三者区别,强调线程安全、可变性和性能,并给出使用建议。这样用户能有一个清晰的比较,方便记忆和应用。</think>--- ### 🚩 核心区别总结 | 特性 | `String` | `StringBuffer` | `StringBuilder` | |--------------------|---------------------------|---------------------------|---------------------------| | **可变性** | 不可变 | 可变 | 可变 | | **线程安全** | 天然安全(不可变) | 安全(同步方法) | 不安全(无同步) | | **性能** | 高频修改性能差 | 线程安全但较 | 单线程下最快 | | **内存开销** | 易产生大量中间对象 | 对象复用 | 对象复用 | | **适用场景** | 常量字符串/少量操作 | 多线程字符串操作 | 单线程字符串操作 | --- ### 1️⃣ **不可变的 `String`** ```java String str = "Hello"; str += " World"; // 实际生成新对象:效率低下 ``` #### 核心特点: - **不可变性**:每次操作都生成新对象(适合缓存、哈希键等场景) - **字符串常量池优化**:相同字面量复用对象(`"a" + "b"` 编译期优化为 `"ab"`) - **线程安全**:无需额外同步(天然线程安全) #### 典型使用场景: - 配置文件读取 - HTTP 请求参数 - 数据库连接字符串等**不需要修改**的场景 --- ### 2️⃣ **线程安全的 `StringBuffer`** ```java StringBuffer sb = new StringBuffer(); sb.append("Hello").append(" World"); // 直接修改原对象 ``` #### 核心特点: - **同步方法**:所有关键方法用 `synchronized` 修饰(线程安全但性能较低) - **初始容量 16**:自动扩容(每次扩容 `旧容量*2 + 2`) - **方法链调用**:支持链式写法(`append().insert().delete()`) #### 典型使用场景: - 多线程环境下的日志拼接 - 并发服务器中的请求处理 - **JDK 1.0** 开始存在(早期方案) --- ### 3️⃣ **高性能的 `StringBuilder`** ```java StringBuilder sb = new StringBuilder(); sb.append("Java").reverse(); // 直接修改原对象 ``` #### 核心特点: - **无同步开销**:方法未加锁(单线程性能比 `StringBuffer` 高约 **10-15%**) - **初始容量 16**:扩容逻辑与 `StringBuffer` 相同 - **JDK 1.5+** 引入:为单线程场景优化 #### 典型使用场景: - 单线程下的 JSON/XML 构建 - 循环体内的字符串处理 - 高频字符串操作(如大数据处理) --- ### ⚡️ 性能对比测试 ```java // 测试代码片段 public void testPerformance() { int N = 100000; // String 测试 long start = System.currentTimeMillis(); String s = ""; for (int i = 0; i < N; i++) { s += i; // 每次循环创建新对象 } System.out.println("String: " + (System.currentTimeMillis() - start) + "ms"); // StringBuffer 测试 start = System.currentTimeMillis(); StringBuffer sb1 = new StringBuffer(); for (int i = 0; i < N; i++) { sb1.append(i); } System.out.println("StringBuffer: " + (System.currentTimeMillis() - start) + "ms"); // StringBuilder 测试 start = System.currentTimeMillis(); StringBuilder sb2 = new StringBuilder(); for (int i = 0; i < N; i++) { sb2.append(i); } System.out.println("StringBuilder: " + (System.currentTimeMillis() - start) + "ms"); } ``` #### 典型结果(10万次循环): - `String`: 约 **5000ms**(产生大量垃圾对象) - `StringBuffer`: 约 **10ms** - `StringBuilder`: 约 **5ms** --- ### 🛠️ 选型建议 1. **优先用 `String`** - 数据不经常变化 - 需要作为常量使用(如 `Map` 的键) 2. **多线程操作 → `StringBuffer`** - 多线程日志处理 - 并发环境下的字符串组装 3. **单线程操作 → `StringBuilder`** - 循环体内字符串操作 - SQL 语句拼接 - 高频字符串处理 --- ### 📌 底层实现揭秘 #### 共有特性(`StringBuffer`/`StringBuilder`): ```java // AbstractStringBuilder 中的核心存储 char[] value; // 动态数组存储字符 int count; // 实际字符数量 ``` #### 扩容机制: 1. 计算新容量 = `原容量*2 + 2` 2. 如果仍不足,直接扩容到**目标长度** 3. 调用 `Arrays.copyOf()` 复制数据 --- ### ❌ 常见误区 1. **错误认知**:`StringBuilder` 在所有场景都比 `StringBuffer` 快 **真相**:在单线程下更快,但在多线程中频繁同步时可能更 2. **错误写法**:在循环中用 `+` 拼接字符串 **修正**:应使用 `StringBuilder`(编译器不会优化循环内的 `+` 操作) 3. **过度优化**:预设容量(仅在超大数据处理时有意义) ```java // 合理预设容量(减少扩容次数) StringBuilder sb = new StringBuilder(1024); ``` --- **总结**:理解三者的差异是写出高效 Java 代码的基础,根据线程安全和性能需求做出精准选择。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值