我有一个字符串,我需要用字典中的值替换标记。它必须尽可能高效。用string.replace进行循环只会消耗内存(字符串是不可变的,请记住)。因为StringBuilder.Replace()旨在用于字符串操作,所以它会更好吗?
我希望避免花费RegEx,但是如果这样可以提高效率,那就可以了。
注意:我不在乎代码的复杂性,只在乎它的运行速度和消耗的内存。
平均状态:长度255-1024个字符,字典中有15-30个键。
我有一个字符串,我需要用字典中的值替换标记。它必须尽可能高效。用string.replace进行循环只会消耗内存(字符串是不可变的,请记住)。因为StringBuilder.Replace()旨在用于字符串操作,所以它会更好吗?
我希望避免花费RegEx,但是如果这样可以提高效率,那就可以了。
注意:我不在乎代码的复杂性,只在乎它的运行速度和消耗的内存。
平均状态:长度255-1024个字符,字典中有15-30个键。
Answers:
使用以下代码使用RedGate Profiler
class Program
{
static string data = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz";
static Dictionary<string, string> values;
static void Main(string[] args)
{
Console.WriteLine("Data length: " + data.Length);
values = new Dictionary<string, string>()
{
{ "ab", "aa" },
{ "jk", "jj" },
{ "lm", "ll" },
{ "yz", "zz" },
{ "ef", "ff" },
{ "st", "uu" },
{ "op", "pp" },
{ "x", "y" }
};
StringReplace(data);
StringBuilderReplace1(data);
StringBuilderReplace2(new StringBuilder(data, data.Length * 2));
Console.ReadKey();
}
private static void StringReplace(string data)
{
foreach(string k in values.Keys)
{
data = data.Replace(k, values[k]);
}
}
private static void StringBuilderReplace1(string data)
{
StringBuilder sb = new StringBuilder(data, data.Length * 2);
foreach (string k in values.Keys)
{
sb.Replace(k, values[k]);
}
}
private static void StringBuilderReplace2(StringBuilder data)
{
foreach (string k in values.Keys)
{
data.Replace(k, values[k]);
}
}
}
字符串长度= 1456
stringbuilder#1在方法中创建stringbuilder,而#2则没有,因此性能差异最终可能是相同的,因为您只是将工作移出了方法。如果您以stringbuilder而不是字符串开头,那么可能会选择#2。
至于内存,使用RedGateMemory事件探查器,您无需担心,直到您进行很多替换操作为止,在这些操作中stringbuilder将会全面获胜。
这可能会有帮助:
简短的答案似乎是String.Replace更快,尽管它可能会对您的内存占用/垃圾回收开销产生更大的影响。
是的,StringBuilder
可以同时提高速度和内存(基本上是因为每次您对其进行操作时,它都不会创建字符串的实例-StringBuilder
始终对同一个对象进行操作)。这是带有一些详细信息的MSDN链接。
这将很大程度上取决于给定字符串中平均存在多少个标记。
在StringBuilder和String之间,搜索键的性能可能相似,但是如果您必须在单个字符串中替换许多标记,则StringBuilder将获胜。
如果平均每个字符串只期望一个或两个标记,而您的字典很小,那我就去买String.Replace。
如果标记很多,则可能需要定义一种自定义语法来标识标记-例如,用合适的转义规则将括号括在括号中。然后,您可以实现一种解析算法,该算法迭代字符串的字符一次,识别并替换找到的每个标记。或使用正则表达式。
MatchEvaluator
来实际执行字典查找。
我在这里花了两分钱,我只写了几行代码来测试每种方法的执行方式,并且按预期,结果是“取决于”的。
对于较长的字符串Regex
似乎表现更好,对于较短的字符串String.Replace
则是如此。我可以看到的用法StringBuilder.Replace
不是很有用,如果使用不当,则可能在GC方面具有致命性(我尝试共享一个实例StringBuilder
)。
@DustinDavis的答案的问题在于,它对相同的字符串进行递归操作。除非您打算进行往返操作,否则在这种测试中,每个操作案例确实应该有单独的对象。
我决定创建自己的测试,因为我在Web上发现了一些矛盾的答案,并且我想完全确定自己。我正在处理的程序处理大量文本(在某些情况下具有成千上万行的文件)。
因此,这是一种快速的方法,您可以复制和粘贴并自己查看哪种方法更快。您可能需要创建自己的文本文件进行测试,但是您可以轻松地从任何地方复制和粘贴文本,并为自己制作足够大的文件:
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Windows;
void StringReplace_vs_StringBuilderReplace( string file, string word1, string word2 )
{
using( FileStream fileStream = new FileStream( file, FileMode.Open, FileAccess.Read ) )
using( StreamReader streamReader = new StreamReader( fileStream, Encoding.UTF8 ) )
{
string text = streamReader.ReadToEnd(),
@string = text;
StringBuilder @StringBuilder = new StringBuilder( text );
int iterations = 10000;
Stopwatch watch1 = new Stopwatch.StartNew();
for( int i = 0; i < iterations; i++ )
if( i % 2 == 0 ) @string = @string.Replace( word1, word2 );
else @string = @string.Replace( word2, word1 );
watch1.Stop();
double stringMilliseconds = watch1.ElapsedMilliseconds;
Stopwatch watch2 = new Stopwatch.StartNew();
for( int i = 0; i < iterations; i++ )
if( i % 2 == 0 ) @StringBuilder = @StringBuilder .Replace( word1, word2 );
else @StringBuilder = @StringBuilder .Replace( word2, word1 );
watch2.Stop();
double StringBuilderMilliseconds = watch1.ElapsedMilliseconds;
MessageBox.Show( string.Format( "string.Replace: {0}\nStringBuilder.Replace: {1}",
stringMilliseconds, StringBuilderMilliseconds ) );
}
}
我得到了那个字符串。每次换出8-10个字母单词时,replace()的速度提高了约20%。如果您需要自己的经验证据,请自己尝试。