1. 按
本文主要用于介绍C#中字符串拼接的三种方法:String 的+
、concat
方法 以及 StringBuilder( or StringBuffer) 的append
方法之间的性能差异。
2. 字符串拼接的三种方法
- 加号
+
; concat
方法;(用法示例可参考:https://blog.csdn.net/COCO56/article/details/102531010)- StringBuilder(或StringBuffer)的
append
方法;
一般来讲,拼接速度:3>>2>1,即方法3远快于方法2,方法2略快于方法1。
3. 原因
-
对+号来说
每趟循环都会创建一个StringBuilder对象 ;
每次执行完毕都会调用toString方法将其转换为字符串 。
不适用于循环操作。
如对于:String str = "My name is "; str = str + "csdn";
相当于 str = new StringBuilder(str).append(“csdn”).toString();
语句执行完之后,总共有三个对象。 -
concat()方法:
concat源代码:public String concat(String str) { // 追加的字符串长度 int otherLen = str.length(); // 如果追加的字符串长度为0,则不做修改,直接返回原字符串 if (otherLen == 0) { return this; } // 获取原字符串的字符数组value的长度 int len = value.length; // 将原字符串的字符数组value放到buf字符数组中 char buf[] = Arrays.copyOf(value, len + otherLen); // 将追加的字符串转化成字符数组,添加到buf中 str.getChars(buf, len); // 产生一个新的字符串并返回 return new String(buf, true); }
整体是一个数组的拷贝,虽然在内存中是处理都是原子性操作,速度非常快,但是,最后的return语句创建一个新String对象,也就是每次concat操作都会创建一个新的String对象,这也是限制concat方法速度的原因。
-
append()方法
append源代码:public AbstractStringBuilder append(String str) { // 如果是null值,则把null作为字符串处理 if (str == null) return appendNull(); int len = str.length(); // 追加后的字符数组长度是否超过当前值 ensureCapacityInternal(count + len); // 字符串复制到目标数组 str.getChars(0, len, value, count); count += len; return this; } private AbstractStringBuilder appendNull() { int c = count; ensureCapacityInternal(c + 4); final char[] value = this.value; value[c++] = 'n'; value[c++] = 'u'; value[c++] = 'l'; value[c++] = 'l'; count = c; return this; } private void ensureCapacityInternal(int minimumCapacity) { // overflow-conscious code if (minimumCapacity - value.length > 0) expandCapacity(minimumCapacity); // 加长,并作数组拷贝 }
整个append方法都在做字符数组的处理,加长,拷贝等,这些都是基本的数据处理,整个方法内并没有生成对象。只是最后toString返回一个对象而已。需要注意的是,append()方法返回的是一个StringBuilder(or StringBuffer)对象实例。
-
总结
大多数情况下,使用“+”即可满足需求,但是当在频繁进行字符串的运算(如拼接、替换、删除等),或者在系统性能临界的时候,我们可以考虑使用concat()或append()方法。
4. 实际测试
正在测试各方法在拼接0到99999之间的数字为一个长字符串的用时:
加号用时:00:00:17.0903268
concat()方法用时:00:00:16.9477968
StringBuilder的append()方法用时:00:00:00.0149635
源码:
using System;
using System.Diagnostics;
using System.Text;
namespace _2_6_字符串拼接性能比较
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("正在测试各方法在拼接0到99999之间的数字为一个长字符串的用时:");
testAdd();
testConcat();
testAppend();
Console.WriteLine("\nPress any key to quit.");
Console.ReadKey();
}
static void testAdd()
{
//需要不断地开辟存储空间
string str = "";
//创建一个计时器,用于记录程序运行时间
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < 100000; i++)
{
str += i;
}
sw.Stop();
//Console.WriteLine(str);
Console.Write("加号用时:");
Console.WriteLine(sw.Elapsed);
}
static void testConcat()
{
string str = "";
Stopwatch sw = new Stopwatch();
sw.Restart();
for (int i = 0; i < 100000; i++)
{
str = String.Concat(str, i);
}
sw.Stop();
//Console.WriteLine(str);
Console.Write("concat()方法用时:");
Console.WriteLine(sw.Elapsed);
}
static void testAppend()
{
//没有开空间,玩的是同一块内存
StringBuilder sb = new StringBuilder();
Stopwatch sw = new Stopwatch();
sw.Restart();
for (int i = 0; i < 100000; i++)
{
sb.Append(i);
}
sw.Stop();
//Console.WriteLine(sb.ToString());
Console.Write("StringBuilder的append()方法用时:");
Console.WriteLine(sw.Elapsed);
}
}
}