使私有方法返回String而不是传递StringBuilder有什么优缺点

考虑一个带有“buildMessage”方法的类(类似于):

public class MessageBuilder { public String buildMessage() { //Build up the message and return // //Get the header //Get the body //Get the footer //return } } 

当我们构建我们的消息时,最好使用StringBuilder(或类似的缓冲对象)构建字符串,而不是仅仅将一堆字符串连接在一起。 但这是否意味着你失去了返回String的好处而不是将StringBuilder作为参数?

换句话说,这很好读,很容易理解:

 private String getHeader() { StringBuilder builder = new StringBuilder(); builder.append("Hello ") .append(this.firstname) .append(",\n"); return builder.toString(); } 

这对我来说比被迫传递给StringBuilder更自然,但我们也可以这样写:

 private void appendHeader(StringBuilder builder) { builder.append("Hello ") .append(this.firstname) .append(",\n"); } 

第一个选项使得可以使用“get”方法,即使意图不是将返回的值附加到缓冲区。 它还使公共方法易于理解:

 public class MessageBuilder { public String buildMessage() { StringBuilder builder = new StringBuilder(); builder.append(getHeader()) .append(getBody()) .append(getFooter()); return builder.toString(); } } 

使用第二个选项时会导致:

 public class MessageBuilder { public String buildMessage() { StringBuilder builder = new StringBuilder(); appendHeader(builder); appendBody(builder); appendFooter(builder); return builder.toString(); } } 

我的问题是第一个选项是否会遇到与“concating”+“strings”+“together”相同的内存问题。 我有兴趣听听关于哪些内容读得更好的意见(因为如果有一个明确的赢家,哪个更清洁,更容易阅读,那将对它有利于它的重要性),但我也对它的效率感到好奇。 我怀疑几乎没有差别,但想知道是否有人“知道”与每种方法相关的成本 – 如果是你,请分享!

重用StringBuilder是更有效的CPU。 但是,我怀疑这真的很重要。

你应该做你认为最自然,最清晰的方法。 (根据你所说的,似乎是StringBuilder)

与许多人的想法相比,单独表现通常不是做某事的好理由。

基本上,在第一种选择中,你会遇到两种截然不同的低效问题。

1)。 您正在创建一个stringBuilder对象3次(总共4次),而在第二个选项中,它只创建一次并重复使用。 对象的创建必须值得,所以通常在对字符串执行多个字符串操作任务时使用stringBuilder。

2)。 因为每个标题,正文和页脚都返回一个字符串,在每种情况下,您创建一个普通字符串并不必要地复制它,这就失去了使用stringBuilder的目的。 基本上,对正常字符串进行仲裁只是从头开始重新创建它,因此您在创建时会遇到相同的开销(因为concating也是从头开始重新创建)。

您可能最好创建一个类,并且在类的构造函数中,您可以创建字符串构建器对象,这样您就不需要在上层代码中执行此操作。 在类中,您可以使用三个方法,每个方法在构建时使用私有字符串构建器对象并最终返回字符串。 通过这种方式,您可以在不传递字符串构建器参数的情况下进行调用,而无需在上层代码中创建对象(根据您的代码,类中会自动处理杂乱的内容,这将使其更易于阅读)。 .. 🙂

我希望这有帮助。 如果您希望我进一步解释,请告诉我。

另外,如果服务器上有大量对这些程序的调用,你应该只担心这些事情,特别是如果在For循环中发生这种情况,如果这两个点都是真的或者适用,则更是如此:)。

队友的欢呼声。

仅仅因为我没有在其他答案中清楚地提到它:

 private String getHeader() { StringBuilder builder = new StringBuilder(); builder.append("Hello ") .append(this.firstname) .append(",\n"); return builder.toString(); } 

可以替换为

 private StringBuilder builder = new StringBuilder(); private String getHeader() { builder.setLength(0); builder.append("Hello ") .append(this.firstname) .append(",\n"); return builder.toString(); } 

缓冲区将分配一次。 缓冲区扩展时,将保留扩展名,直到声明对象被垃圾回收。 因此,重新分配和调整缓冲区大小没有时间丢失。

只有小故障是您可能需要使方法同步(即线程安全)。

使用现有的字符串缓冲区比字符串更快,因为不需要分配。 字符串的手动连接非常昂贵,因为(通常)需要多次分配。

PS:当然,字符串构建器的私有分配可以在私有方法之间共享,这意味着最终用户不需要自己分配它。 在某些情况下,传递缓冲区使代码更易于阅读和更多function。 这里没有明确的答案或规则,这取决于你在做什么和你需要什么。

妄想

如果StringBuilder不是最终类,那就太好了。 将append …方法放在扩展StringBuilder的内部类中:

 public class MessageBuilder { public String buildMessage() { return "" + MyStringBuilder() .appendHeader() .appendBody() .appendFooter(); } private class MyBuilder extends StringBuilder { MyBuilder appendHeader() { append("Hello ") .append(this.firstname) .append(",\n"); } } 

实际工作的方法 – 还有更多工作 – 是为StringBuilder编写一个(非final!)委托类,并实现像这样的私有特殊用途扩展作为它的子类。 见这里的讨论。

如果“StringBuilder”将隐藏在实例变量中,则最后一种方法将是真正的构建器模式。 使用该设计模式(第一个示例不是 designpattern Builder)的原因是能够创建覆盖appendHeader / appendFooter和appendBody方法的派生类(例如,您希望构建小的HTML片段。如果您只想要优雅地构造一个字符串,然后流畅的例子(倒数第二个)看起来最好,并且可能是最容易理解的年轻程序员。