c#:插入字符串是另一个性能问题

本文关键字:另一个 性能 问题 字符串 插入 | 更新日期: 2023-09-27 18:06:55

我有一个字符串,它很长,还有一个索引和值的排序字典。我应该遍历字典中的元素并将值插入到字符串中指定的索引中。我编写了以下代码,运行良好,但非常慢:

private string restoreText(string text){
  StringBuilder sb = new StringBuilder(text);
  foreach(KeyValuePair<int, string> pair in _tags){
    sb.Insert(pair.Key, pair.Value);
  }
  return sb.ToString();
}

字典可能非常大,包含500,000个元素。我认为使这个函数变慢的是Insert()方法。对于一个有100,000个元素的字典,它几乎花了5秒。

是否有更有效的方法来编写这个方法?

谢谢,玛雅

c#:插入字符串是另一个性能问题

更好的方法是对要插入的项进行排序,然后一个接一个地添加它们。

既然你没有评论重叠,也许你一开始就把你的项目排序了?

您的原始代码将根据从_tags返回的项的顺序给出不同的结果;我很怀疑这不是你的本意。

相反,将标签按顺序排序,然后按正确的顺序将它们添加到字符串构建器中:

private string restoreText(string text)
{
    StringBuilder sb = new StringBuilder();
    foreach( KeyValuePair<int, string> pair in _tags.OrderBy(t => t.Key))
    {
        sb.Append(pair.Value);
    }
    return sb.ToString();
}

如果您真的想让它尽可能快地运行,请预先初始化StringBuilder的容量:

    StringBuilder sb = new StringBuilder(_tags.Sum(k => k.Value.Length));

我错过了最初用于初始化StringBuildertext参数。

为了避免在内存中打乱文本(由StringBuilder.Insert()引起),我们希望坚持使用StringBuilder.Append()

我们可以将原始文本转换为另一个KeyValuePair实例序列,将它们与原始列表合并并按顺序处理。

它看起来像这样(注意: adhoc code):

private string restoreText(string text)
{
    var textPairs 
        = text.Select( (c,i) => new KeyValuePair<int,string>(i, (string)c));
    var fullSequence
        = textPairs.Union(_tags).OrderBy(t => t.Key);
    StringBuilder sb = new StringBuilder();
    foreach( KeyValuePair<int, string> pair in fullSequence)
    {
        sb.Append(pair.Value);
    }
    return sb.ToString();
}

注意-我对你的上下文做了一大堆假设,所以这可能不太适合你。特别要注意的是,.Union()将丢弃重复项,尽管有简单的解决方法。

如果你设置了索引,那么插入不会改变其他索引,但是当你的代码说"是"时,我也会假设这样做。

你能测试一下这个吗?

private string RestoreText(string text)
{
    var sb = new StringBuilder();
    var totalLen = 0;
    var orgIndex = 0;
    foreach (var pair in _tags.OrderBy(t => t.Key))
    {
        var toAdd = text.Substring(orgIndex, pair.Key - totalLen);
        sb.Append(toAdd);
        orgIndex += toAdd.Length;
        totalLen += toAdd.Length;
        sb.Append(pair.Value);
        totalLen += pair.Value.Length;
    }
    if (orgIndex < text.Length) sb.Append(text.Substring(orgIndex));
    return sb.ToString();
}

它只在与原始代码相同的情况下使用append

我不知道你的数据怎么样。

但是在我的测试中,它运行得很快(564ms)。

        Dictionary<int, string> _tags = new Dictionary<int, string>();
        for (int i = 0; i < 1000000; i++)
        {
            _tags.Add(i, i.ToString().Length + "");
        }
        string text = new String('a' , 50000000);
        Console.WriteLine("****************************************");
        System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew();
        StringBuilder sb = new StringBuilder(text);
        foreach (KeyValuePair<int, string> pair in _tags)
        {
            sb.Insert(pair.Key, pair.Value);
        }
        sw.Stop();
        Console.WriteLine("sw:" + sw.ElapsedMilliseconds);
        Console.ReadKey();

如果你可以使用append()而不是insert(),它只需要35ms…