【问题标题】:Java concatenate to build string or formatJava 连接以构建字符串或格式
【发布时间】:2011-10-15 02:40:14
【问题描述】:

我目前正在使用 java 编写一个 MUD(基于文本的游戏)。 MUD 的主要方面之一是格式化字符串并将其发送回用户。这将如何最好地完成?

假设我想发送以下字符串:

你对某人说“你好!” - 其中“某人”、“说”和“你好!”都是变量。哪个性能最好?

"You " + verb + " to " + user + " \"" + text + "\""

String.format("You %1$s to %2$s \"%3$s\"", verb, user, text)

或其他选择?

我不确定最终哪个会更容易使用(这很重要,因为它无处不在),但我现在正在考虑它,因为与 + 连接会得到一个与一些较大的线路有点混淆。我觉得在这种情况下使用 StringBuilder 只会使其可读性降低。

这里有什么建议吗?

【问题讨论】:

  • 不要担心性能,除非您发现这是一个问题并且这是瓶颈。做任何你认为会产生最具可读性和可维护性的代码的事情。

标签: java string-formatting string-concatenation mud


【解决方案1】:

如果字符串是使用单个连接表达式构建的;例如

String s = "You " + verb + " to " + user + " \"" + text + "\"";

那么这或多或少等同于冗长:

StringBuilder sb = new StringBuilder();
sb.append("You");
sb.append(verb);
sb.append(" to ");
sb.append(user);
sb.append(" \"");
sb.append(text );
sb.append('"');
String s = sb.toString();

其实经典的Java编译器会把前者编译成后者……差不多。在 Java 9 中,他们实现了 JEP 280,它将字节码中的构造函数和方法调用序列替换为单个 invokedynamic 字节码。然后运行时系统优化这个1

当您开始创建中间字符串或使用+= 等构建字符串时,就会出现效率问题。此时,StringBuilder 变得更加高效,因为您减少了创建然后丢弃的中间字符串的数量。

现在,当您使用 String.format() 时,它应该在后台使用 StringBuilder。但是,format 还必须在每次调用时解析格式字符串,如果您以最佳方式构建字符串,则不会产生这样的开销。


话虽如此,我的建议是以最易读的方式编写代码。如果 profiling 告诉您这是一个真正的性能问题,则只需担心构建字符串的最有效方法。 (现在,您正在花时间思考如何解决可能被证明无关紧要或无关紧要的性能问题。)

另一个答案提到使用格式字符串可以简化对多种语言的支持。确实如此,尽管在诸如复数、性别等方面您可以做的事情是有限的。


1 - 因此,对于 Java 9 或更高版本,按照上面的示例进行手部优化实际上可能会产生负面后果。但这是您在进行微优化时要承担的风险。

【讨论】:

  • 感谢您的详细解释,它帮助我思考问题。我将使用 String.format,因为它是 a) 内置解决方案 b) 通过使字符串更具可读性,已经让我的生活更轻松。
【解决方案2】:

与加号连接,编译器可以以执行方式转换代码。使用字符串格式我不知道。

我更喜欢与 plus 并列,我认为这更容易理解。

【讨论】:

    【解决方案3】:

    保持简单的关键是永远不要看它。这就是我的意思:

    Joiner join = Joiner.on(" ");
    
    public void constructMessage(StringBuilder sb, Iterable<String> words) {
      join.appendTo(sb, words);
    }
    

    我正在使用 Guava Joiner 类使可读性不再是问题。还有什么比“加入”更清楚的呢?所有关于连接的讨厌的部分都被很好地隐藏起来了。通过使用 Iterable,我可以将这种方法用于各种数据结构,列表是最明显的。

    这是一个使用 Guava ImmutableList 的调用示例(它比常规列表更有效,因为任何修改列表的方法都只会抛出异常,并且正确地表示了constructMessage() 无法更改单词列表的事实, 消费它):

    StringBuilder outputMessage = new StringBuilder();
    constructMessage(outputMessage, 
             new ImmutableList.Builder<String>()
                .add("You", verb, "to", user, "\"", text, "\"")
                .build());
    

    【讨论】:

      【解决方案4】:

      我认为与+ 连接比使用String.format 更具可读性。

      String.format 适用于需要格式化数字和日期的情况。

      【讨论】:

      • 似乎是最简洁和方便的选择,它是通过java.util.Formatter实现的。
      【解决方案5】:

      老实说,如果您想减少打字,我建议您使用第一个,如果您正在寻找一种更 C 风格的方式,则建议您使用后一个。

      我在这里坐了一两分钟,思考可能是什么问题,但我认为这取决于你想输入多少。

      其他人有想法吗?

      【讨论】:

      • 我的理由更多是关于可读性。我非常习惯于输入“东西”+“其他东西”,如果我选择另一种选择,我可能不得不打破这种习惯。我最初的想法是像“你 [动词] 到 [用户] [文本]”并做一个替换(类驱动,所以我会添加像 setElement(“动词”,动词)这样的变量,然后它会吐返回一个格式化的字符串),但偶然发现 .format 并认为我最初的想法在性能上会很糟糕(但超级容易阅读)。
      【解决方案6】:

      假设您要经常重用基本字符串 将您的模板存储为

      String mystring = "你 $1 到 $2 \"$3\""

      然后只需获取一份副本,然后将 $X 替换为您想要的。

      这也适用于资源文件。

      【讨论】:

      【解决方案7】:

      我认为 String.format 看起来更干净。 但是你可以使用 StringBuilder 并使用 append 函数来创建你想要的字符串

      【讨论】:

        【解决方案8】:

        就性能而言,最好的可能是使用 StringBuffer。

        【讨论】:

        • 不同意。来自StringBuilder javadoc:在可能的情况下,建议优先使用此类而不是StringBuffer,因为它在大多数实现下会更快。 --- 顺便说一句,我怀疑字符串连接是此类应用程序的瓶颈。
        • 相同的差异,一个同步,一个不同步。正确实施同步后,如果没有争用,差异可以忽略不计。 (而且,很明显,如果存在争用,就不想使用 StringBuilder。)
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-01-06
        • 1970-01-01
        • 2014-10-25
        • 1970-01-01
        • 2021-07-06
        • 2015-12-02
        • 1970-01-01
        相关资源
        最近更新 更多