使用多个字符串构建器来构建一个字符串

5

我主要是C#和Java开发人员,但这个问题可能适用于任何使用StringBuilder或类似技术的编程语言。

大家都知道,即使连接少量字符串也会严重影响性能(尽管这一点有争议)。我的问题是,有没有人了解在StringBuilder中使用另一个StringBuilder的性能影响。为了阐明我的意思,让我包含一些虚拟代码来帮助说明我的观点。

此外,我意识到调用多个StringBuilder实例自然会影响性能,但我不认为我会调用足够多的实例来导致任何真正的性能问题。(这个假设也可能是错误的,任何意见都很有帮助)

public string StringToGet()
{
     StringBuilder sb = new StringBuilder("Some Text to add");
     sb.append("more text to add");
     sb.append("more text to add");
     sb.append("more text to add");
     sb.append("more text to add");
     sb.append("more text to add");
     sb.append("more text to add");
     sb.append("more text to add");
     sb.append(MethodThatCreatesAnotherString());
     sb.append("more text to add");
     sb.append("more text to add");
     sb.append("more text to add");
     sb.append("more text to add");
     sb.append("more text to add");
     sb.append("more text to add");
     sb.append("more text to add");
     sb.append(MethodThatCreatesAnotherString());
     sb.append("more text to add");
     sb.append("more text to add");
     sb.append("more text to add");
     sb.append("more text to add");
     sb.append("more text to add");
     sb.append("more text to add");
     sb.append("more text to add");
     sb.append(MethodThatCreatesAnotherString());
     //etc   

     return sb.toString();

}

private string MethodThatCreatesAnotherString()
{
     StringBuilder sb = new StringBuilder("Other text");
     sb.append("more text to add");
     sb.append("more text to add");
     sb.append("more text to add");
     sb.append("more text to add");
     sb.append("more text to add");
     sb.append("more text to add");
     sb.append("more text to add");

     return sb.ToString();
}

逻辑告诉我这不应该是一个问题,但这种方法在某些方面似乎并不正确。 有人能回答以下问题吗?

  1. 相比于仅使用单个StringBuilder而不使用其他方法,这是否会造成显著的性能损失?
  2. 这是一个合理可接受的做法吗?

非常感谢您提供任何帮助。


这个问题应该得到更多的认可。这种算法肯定有其应用价值。 - Panzercrisis
3个回答

9

通常,如果直接字符串连接的次数超过5次,最好使用StringBuilder。 如果您可以控制依次构建字符串的方法,为什么不考虑将StringBuilder传递到该方法中呢?

public void MethodThatCreatesAnotherString(StringBuilder sb)
{
   // code that appends to StringBuilder
}

因此,在调用该函数时,您将当前的StringBuilder传递进去,并且不会损失性能,但可能会牺牲一些可读性。
话虽如此,除非您需要进行数百或数千次此类操作,否则不必担心过早优化。主要的性能问题在于必须执行两次操作(即在方法内部附加字符串,然后附加完整字符串),但仍然不会像使用“+”连接符那样受到性能影响。
编辑:以下是一个说明性的示例。
string SomeMethodCreatingAString()
{
   StringBuilder sb = new StringBuilder();
   sb.append("more text to add");
   sb.append("more text to add");
   sb.append("more text to add");
   sb.append("more text to add");
   sb.append("more text to add");
   MethodThatCreatesAnotherString(sb);
   // more text
   return sb.ToString();
}

因此,在第二种方法中,您不会添加该字符串,而是仅在该方法中使用相同的StringBuilder。希望这能让您理解。

那是一个非常好的观点,drharris和@Scott Munro。我有点尴尬地说,我没有考虑到这一点。根据这种方法,原始字符串构建器似乎只把Append(SomeMethod())视为另一个字符串,这基本上回答了我的问题。 - pat8719
我意识到我没有给出如何调用它的实际指令。希望你理解了我的意图,但以防其他人出现,我添加了一个示例。不要担心不考虑它;这是像其他所有设计决策一样的设计决策,并不总是最好的方法。通过返回“void”,该方法隐藏了它实际执行的操作,这有时不是最佳选择。有时,您更愿意花费额外的毫秒来获得更清晰的代码性能。 - drharris
我明白你的意图,但还是谢谢。根据所有这些答案的普遍意见,为了保持代码清洁,最好避免在方法中使用'void'返回,特别是考虑到性能影响可能非常小。 - pat8719

4

我不确定性能方面的影响,但是将您的MethodThatCreatesAnotherString方法的签名更改为接受一个StringBuilder,可以消除需要多个StringBuilder实例的情况。


3
http://www.codinghorror.com/blog/2009/01/the-sad-tragedy-of-micro-optimization-theater.html所示,不同字符串拼接方式之间的差异在大量拼接时才会变得非常微小。因此,除非您已经知道代码存在性能问题,否则我认为这很好。您的例子中只增加了3个额外的字符串构建器初始化和3个额外的字符串分配。从长远来看,您应该尽量简单明了,并且只有在确信需要进行复杂的微优化并测试结果之后才去担心它们。

+1 这确实是一个公正的观点,但由于我本能地倾向于使用 StringBuilder,我的问题实际上是想确保使用所谓的“嵌入式” StringBuilder 没有绝对的错误。谢谢您分享这篇文章。 - pat8719

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接