C#字符串拼接的⼏种⽅式
C#字符串连接常⽤的四种⽅式:StringBuilder、+、string.Format、List。
1.+的⽅式
string sql = “update tableName set int1=” + int1.ToString() + “,int2=” + int2.ToString() + “,int3=” + int3.ToString() + " where id=" + id.ToString();
编译器会优化为:
string sql = string.Concat(new string[]{});sql优化的几种方式
通过分析string.Concat(params string[] values)的实现可以知道:先计算⽬标字符串的长度,然后申请相应的空间,最后逐⼀复制,时间复杂度为o(n),常数为1。
固定数量的字符串连接效率最⾼的是+。
但是字符串的连+不要拆成多条语句,⽐如:
string sql = “update tableName set int1=”;
sql += int1.ToString();
sql += …
这样的代码,不会被优化为string.Concat,就变成了性能杀⼿,因为第i个字符串需要复制n-i次,时间复杂度就成了o(n^2)。
2.StringBuilder的⽅式
StringBuilder 只分配⼀次内存,如果第⼆次连接内存不⾜,则修改内存⼤⼩;它每次默认分配16字节,如果内存不⾜,则扩展到32字节,如果仍然不⾜,继续成倍扩展。
如果频繁的扩展内存,效率⼤打折扣,因为分配内存,时间开销相对⽐较⼤。如果事先能准确估计程序执⾏过程中所需要的内存,从⽽⼀次分配⾜内存,效率⼤⼤提⾼。
如果字符串的数量不固定,就⽤StringBuilder,⼀般情况下它使⽤2n的空间来保证o(n)的整体时间复杂度,常数项接近于2。
因为这个算法的实⽤与⾼效,类库⾥⾯有很多动态集合都采⽤这种牺牲空间换取时间的⽅式,⼀般来说效果还是不错的。
3.string.Format的⽅式
它的底层是StringBuilder,所以其效率与StringBuiler相似。
public static string Format(IFormatProvider provider,string format,params object[] args)
{
if((format ==null)||(args ==null))
{
throw new ArgumentNullException((format ==null)?"format":"args");
}
StringBuilder sb = StringBuilderCache.Acquire(format.Length +(args.Length *8));
sb.AppendFormat(provider, format, args);
return StringBuilderCache.GetStringAndRelease(sb);
}
4.List它可以转换为string[]后使⽤string.Concat或string.Join,很多时候效率⽐StringBuiler更⾼效。List与StringBuilder采⽤的是同样的动态集合算法,时间复杂度也是O(n),与StringBuilder不同的是:List的n是字符串的数量,复制的是字符串的引⽤;StringBuilder的n 是字符串的长度,复制的数据。不同的特性决定的它们各⾃的适应环境,当⼦串⽐较⼤时建议使⽤List,因为复制引⽤⽐复制数据划算。⽽当⼦串⽐较⼩,⽐如平均长度⼩于8,特别是⼀个⼀个的字符,建议使⽤StringBuilder。
MSDN中关于StringBuilder的性能注意事项:
Concat 和 AppendFormat ⽅法都将新数据串连到⼀个现有的 String 或 StringBuilder 对象。String 对象串联操作总是⽤现有字符串和新数据创建新的对象。StringBuilder 对象维护⼀个缓冲区,以便容纳新数据的串联。如果有⾜够的空间,新数据将被追加到缓冲区的末尾;否则,将分配⼀个新的、更⼤的缓冲区,原始缓冲区中的数据被复制到新的缓冲区,然后将新数据追加到新的缓冲区。
String 或 StringBuilder 对象的串联操作的性能取决于内存分配的发⽣频率。String 串联操作每次都分配内存,⽽ StringBuilder 串联操作仅当 StringBuilder 对象缓冲区太⼩⽽⽆法容纳新数据时才分配内存。因此,如果串联固定数量的 String 对象,则 String 类更适合串联操作。这种情况下,编译器甚⾄会将各个串联操作组合到⼀个操作中。如果串联任意数量的字符串,则 StringBuilder 对象更适合串联
操作;例如,某个循环对⽤户输⼊的任意数量的字符串进⾏串联。