我们必须开门见山地说,在相同字符串的许多操作上,使用
StringBuilder
类别会比使用一个
String
对象来得更有效率。
<?xml:namespace prefix = o />
大家务必了解,
System.String
数据类型(或是说
String
对象)代表的是一种不变的字符串,也就是说,一旦您设定其值,您就不能更改它。如果您尝试要插入、删除或更改字符串的任何部分,唯一的方式就是去建立一个新的字符串。说得更专业点,每次字符串数据变更时,内存中该字符串原来的表示法就会被破坏掉,并建立内含新字符串数据的新表示法,此举会引发对内存的配置作业以及对内存的反配置作业。当然,这些作业都是在幕后完成的,因此真正的成本并不会立刻显现。配置与反配置内存会加重
Common Language Runtime
(
CLR
)里内存管理和内存回收的相关工作,所以绝对是要付出代价的,直接的影响,就是增了处理时间。这种情况在迅速地接连配置和反配置占有大块内存的字符串时尤其明显,就像在大量字符串串连时所发生的情况一样。虽然这种情形在单一用户环境里不会带来任何问题,但是在服务器的环境里使用时(比方说,在
Web
服务器上执行的
ASP.NET®
应用程序中),却可能在效能与延展性上造成极为严重的问题。
以下面这一段程序代码而言(请参阅
CH3_DemoForm017.vb
之「使用
String
」
按钮的
Click
事件处理例程),您知道在每一次的循环中,会发生多少次的字符串配置作业吗?答案是
14
。在此种写法中,「
&
」(或「
+
」)运算子会使得变量
sXml
所指向的字符串被破坏掉然后再重新建立。再次提醒您,字符串配置是很花时间的,而且随着字符串的增长,情况会越来越严重。这正是
.NET Framework
为什么要提供
StringBuilder
类别的原因:
Dim nRep As Int32
Dim Reps As Int32 = Convert.ToInt32(numLoops.Value)
Dim Start, Finish As Double
Start = Microsoft.VisualBasic.DateAndTime.Timer
Dim sXml As String = ""
For nRep = 1 To Reps
sXml &= "< 订单 订单号码 =""" _
& nRep _
& """ 订货日期 =""" _
& DateTime.Now.ToString() _
& """ 客户编号 =""" _
& nRep _
& """ 产品编号 =""" _
& nRep _
& """ 产品说明 =""" _
& " 此产品的 Id 是 : " _
& nRep _
& """ 数量 =""" _
& nRep _
& """/>"
Next nRep
sXml = "< 订单 方法 =""1"">" & sXml & "</ 订单 >"
Finish = Microsoft.VisualBasic.DateAndTime.Timer
txtStringResult.Text = (Finish – Start).ToString
Dim Reps As Int32 = Convert.ToInt32(numLoops.Value)
Dim Start, Finish As Double
Start = Microsoft.VisualBasic.DateAndTime.Timer
Dim sXml As String = ""
For nRep = 1 To Reps
sXml &= "< 订单 订单号码 =""" _
& nRep _
& """ 订货日期 =""" _
& DateTime.Now.ToString() _
& """ 客户编号 =""" _
& nRep _
& """ 产品编号 =""" _
& nRep _
& """ 产品说明 =""" _
& " 此产品的 Id 是 : " _
& nRep _
& """ 数量 =""" _
& nRep _
& """/>"
Next nRep
sXml = "< 订单 方法 =""1"">" & sXml & "</ 订单 >"
Finish = Microsoft.VisualBasic.DateAndTime.Timer
txtStringResult.Text = (Finish – Start).ToString
与
System.String
类别相较之下,
System.Text.StringBuilder
类别则会保留它自己的字符串缓冲区。在针对
StringBuilder
执行作业因而可能会改变字符串数据的长度时,
StringBuilder
会先检查缓冲区的大小是否足够容纳新的字符串数据。如果不够,则缓冲区的大小就会增加预先决定的数量。由于大幅降低内存配置操作的发生机率,因此自然能有效提升效能。
就以字符串串连作业而言,标准的串连方式会导致为每一项串连作业建立一个新字符串,但是
StringBuilder
却每次都使用相同的字符串缓冲区。基本上,当您只要修改字符串,但是不要建立新的字符串对象时,就应该使用
System.Text
命名空间中的
StringBuilder
类别。举例来说,当需要在循环中串连多个字符串时,使用
StringBuilder
类别将可具体提升效能。此外,
StringBuilder
类别还提供了非常有效率的
Replace
方法,它可以用来取代
String.Replace
。
基本上,
StringBuilder
方法在执行上会比标准串连方式优异的原因取决于几项因素,其中包括串连的数目、所要建置之字符串的大小、以及
StringBuilder
的缓冲区初始化参数是否设定得够好。请注意,在大多数的情况下,多估算一些缓冲区空间要比后来又不断加大来得更好。
以先前在循环中进行字符串串连作业的程序代码来说,如果使用
StringBuilder
将其改写如下(请参阅
CH3_DemoForm017.vb
之「使用
StringBuilder
」
按钮的
Click
事件处理例程),将能够大幅提升执行速度,而且如果循环的数目愈多,提升的幅度愈明显:
Dim nRep As Int32
Dim Reps As Int32 = Convert.ToInt32(numLoops.Value)
Dim Start, Finish As Double
Start = Microsoft.VisualBasic.DateAndTime.Timer
Dim oSB As StringBuilder
' 确保 StringBuilder 的容量足以容纳最终的文字
oSB = New StringBuilder(Reps * 165)
oSB.Append("< 订单 方法 =""2"">")
For nRep = 1 To Reps
oSB.Append("< 订单 订单号码 =""")
oSB.Append(nRep)
oSB.Append(""" 订货日期 =""")
oSB.Append(DateTime.Now.ToString())
oSB.Append(""" 客户编号 =""")
oSB.Append(nRep)
oSB.Append(""" 产品编号 =""")
oSB.Append(nRep)
oSB.Append(""" 产品说明 =""")
oSB.Append(" 此产品的 Id 是 : ")
oSB.Append(nRep)
oSB.Append(""" 数量 =""")
oSB.Append(nRep)
oSB.Append("""/>")
Next nRep
oSB.Append("</ 订单 >")
Finish = Microsoft.VisualBasic.DateAndTime.Timer
txtStringBuilderResult.Text = (Finish – Start).ToString
Dim Reps As Int32 = Convert.ToInt32(numLoops.Value)
Dim Start, Finish As Double
Start = Microsoft.VisualBasic.DateAndTime.Timer
Dim oSB As StringBuilder
' 确保 StringBuilder 的容量足以容纳最终的文字
oSB = New StringBuilder(Reps * 165)
oSB.Append("< 订单 方法 =""2"">")
For nRep = 1 To Reps
oSB.Append("< 订单 订单号码 =""")
oSB.Append(nRep)
oSB.Append(""" 订货日期 =""")
oSB.Append(DateTime.Now.ToString())
oSB.Append(""" 客户编号 =""")
oSB.Append(nRep)
oSB.Append(""" 产品编号 =""")
oSB.Append(nRep)
oSB.Append(""" 产品说明 =""")
oSB.Append(" 此产品的 Id 是 : ")
oSB.Append(nRep)
oSB.Append(""" 数量 =""")
oSB.Append(nRep)
oSB.Append("""/>")
Next nRep
oSB.Append("</ 订单 >")
Finish = Microsoft.VisualBasic.DateAndTime.Timer
txtStringBuilderResult.Text = (Finish – Start).ToString
![](https://i-blog.csdnimg.cn/blog_migrate/973eb6db75d18a5080a028d636de88fd.jpeg)
图表1
图表
1
所示者是程序范例
CH3_DemoForm017.vb
的执行画面,其主要目的在于让您比较先前所列之两种字符串串连作业写法(
String
vs StringBuilder
)在执行速度上的差异。您可以先设定循环数,然后依序按一下「使用
String
」
按钮与「使用
StringBuilder
」
按钮,从「处理时间」文字方块中所列的数据可以发现,使用
StringBuilder
类别进行字符串串连作业的执行速度要比使用
String
类别快上好几十倍,可说是天壤之别。
请注意:
欲使用 StringBuilder 类别,请记得汇入命名空间 System.Text 。
欲使用 StringBuilder 类别,请记得汇入命名空间 System.Text 。
不过我们也必须坦白地说,
StringBuilder
类别虽然好用,然而它也也非万能。事实上,并非所有的
String
类别方法都可以直接使用
StringBuilder
来完成,当您恰巧需要
String
类别所独有的方法时(例如:
IndexOf
),您必须将内容提取成一个文字符串,针对字符串执行您所需的操作,然后再继续使用
StringBuilder
。
图表
2
所示者是程序范例
CH3_DemoForm018.vb
的执行画面,它示范如何分别使用
StringBuilder
类别与
String
类别来完成同一项作业。图表
3
同时列出这两种写法的程序代码,以方便您比较之间的差异。本范例特别值得一提的地方是,由于我们要在单字“
MVP
”之后插入一个句点,因此必须先找到此单字,然后再于该单字之后插入一个句点。欲找到此单字,您必须使用
IndexOf
方法来加以搜寻,但是
StringBuilder
并未提供此方法,所以我们只好使用
ToString
方法来取得字符串,然后处理该字符串。
![](https://i-blog.csdnimg.cn/blog_migrate/1c0ca1c67c6cc61bc4d1cf5bf0b0811d.jpeg)
图表2
使用StringBuilder类别
使用String类别
' 示范如何使用StringBuilder类别。
' StringBuilder是由System.Text命名空
' 间所提供,我们已在程序的开头处汇
' 入此命名空间(Imports System.Text)。
Dim sb As New StringBuilder( _
"台湾微软技术社群暨" & _
"最有价值专家 MVP")
sb.Insert(19, _
"(Most Valuable Professional)")
sb.Remove(9, 6)
sb.Replace("技术社群", " Community ")
sb.AppendFormat( _
"{0}年内{1}度当选。", "一", "两")
' 假设您要在单字 "MVP" 之后插入
' 一个句点,您必须先找到此单字,然
' 后再于该单字之后插入一个句点。欲
' 找到此单字,您必须使用IndexOf来
' 加以搜寻,但是StringBuilder并未提
' 供此方法。因此,您务必使用ToString
' 方法来取得字符串,然后处理该字符串。
Dim intPos As Integer
intPos = sb.ToString.IndexOf("MVP")
If intPos > 0 Then
' 插入逗点的位置就是您所搜寻到
' 的位置再加上单字 "MVP" 的长
' 度。
sb.Insert( _
intPos + "MVP".Length, ", ")
End If
txtResults.AppendText( _
Environment.NewLine & _
"StringBuilder 输出:" & _
Environment.NewLine & _
sb.ToString & Environment.NewLine)
' StringBuilder是由System.Text命名空
' 间所提供,我们已在程序的开头处汇
' 入此命名空间(Imports System.Text)。
Dim sb As New StringBuilder( _
"台湾微软技术社群暨" & _
"最有价值专家 MVP")
sb.Insert(19, _
"(Most Valuable Professional)")
sb.Remove(9, 6)
sb.Replace("技术社群", " Community ")
sb.AppendFormat( _
"{0}年内{1}度当选。", "一", "两")
' 假设您要在单字 "MVP" 之后插入
' 一个句点,您必须先找到此单字,然
' 后再于该单字之后插入一个句点。欲
' 找到此单字,您必须使用IndexOf来
' 加以搜寻,但是StringBuilder并未提
' 供此方法。因此,您务必使用ToString
' 方法来取得字符串,然后处理该字符串。
Dim intPos As Integer
intPos = sb.ToString.IndexOf("MVP")
If intPos > 0 Then
' 插入逗点的位置就是您所搜寻到
' 的位置再加上单字 "MVP" 的长
' 度。
sb.Insert( _
intPos + "MVP".Length, ", ")
End If
txtResults.AppendText( _
Environment.NewLine & _
"StringBuilder 输出:" & _
Environment.NewLine & _
sb.ToString & Environment.NewLine)
' 同样的程序代码如果直接使用String物
' 件来处理,将如下所示。请注意到
' .NET Framework 需要去建立新字符串
' 的次数。StringBuilder 显然会比此种
' 操作来得更有效率。
Dim str As String = _
"台湾微软技术社群暨" & _
"最有价值专家 MVP"
str = str.Insert(19, _
"(Most Valuable Professional)")
str = str.Remove(9, 6)
str = str.Replace( _
"技术社群", " Community ")
str &= String.Format( _
"{0}年内{1}度当选。", "一", "两")
Dim intPos As Integer
intPos = str.IndexOf("MVP")
If intPos > 0 Then
str = str.Insert( _
intPos + "MVP".Length, ", ")
End If
txtResults.AppendText( _
Environment.NewLine & _
"String 输出:" & _
Environment.NewLine & _
str & Environment.NewLine)
' 件来处理,将如下所示。请注意到
' .NET Framework 需要去建立新字符串
' 的次数。StringBuilder 显然会比此种
' 操作来得更有效率。
Dim str As String = _
"台湾微软技术社群暨" & _
"最有价值专家 MVP"
str = str.Insert(19, _
"(Most Valuable Professional)")
str = str.Remove(9, 6)
str = str.Replace( _
"技术社群", " Community ")
str &= String.Format( _
"{0}年内{1}度当选。", "一", "两")
Dim intPos As Integer
intPos = str.IndexOf("MVP")
If intPos > 0 Then
str = str.Insert( _
intPos + "MVP".Length, ", ")
End If
txtResults.AppendText( _
Environment.NewLine & _
"String 输出:" & _
Environment.NewLine & _
str & Environment.NewLine)
图表3、String类别与StringBuilder类别使用比较
经由以上的说明,相信大家都对StringBuilder都已经抱持一个非常肯定的态度。
转载于:https://blog.51cto.com/liminzhang/53230