【问题标题】:Is there a "fastest way" to construct Strings in Java?有没有“最快的方法”在 Java 中构造字符串?
【发布时间】:2009-12-03 18:47:06
【问题描述】:

我通常通过以下方式在 Java 中创建字符串:

String foo = "123456";

但是,我的讲师坚持要我使用format 方法形成一个字符串,如下所示:

String foo = String.format("%s", 123456);

速度更快。

另外,他说使用 StringBuilder 类更快。

StringBuilder sb = new StringBuilder(); String foo = sb.append(String.format("%s", 123456)).toString();



如果有的话,哪种方法是最快的创建字符串的方法?

它们不可能 100% 准确,因为我可能记不太清了。

【问题讨论】:

  • 作为一名学生,如果你能想出一种方法来检验他的理论,那就太好了。你能写一个 Java 类来构造 100000 个字符串/字符串缓冲区等并计时吗?
  • 我会告诉你的教授,在现实世界中,非常非常非常小的性能优势远远超过了可读性。此外,应始终首先编写代码以使其尽可能简单易用,然后在需要时进行优化(通常不需要)。
  • 在我看来,为了最快的字符串创建而不断地隐藏代码是不值得的。此外,从 java 版本到 java 版本的最快连接方式是什么,至少连接,字符串是最快的,所以升级时你的仔细优化可能会在你的脸上爆炸。我敢肯定,很多非常高效运行的代码都像您的第一个示例一样初始化了它们的字符串,所以我建议您继续以这种方式初始化它们。
  • 我 100% 同意开发人员性能(可读性)优于软件性能(微优化)。但是,不管怎样,你的老师错了。通过几个方法调用从一个字符串创建一个新的字符串怎么可能比仅仅引用一个已经在常量池中的字符串实例更快?
  • 2 行代码:正在创建 3 个对象(如果我计算正确的话),正在调用 4 个方法,一个自动装箱(+1 对象,+1 方法)并且仍然使用另一个常量字符串 "%s" 是比仅使用常量字符串快得多?

标签: java string


【解决方案1】:

如果只有一个字符串,那么:

String foo = "123456";

最快。您会注意到String.format 行中声明了"%s%",所以我不明白讲师怎么可能认为这更快。此外,您还有一个方法调用。

但是,如果您要随着时间的推移构建字符串,例如在 for 循环中,那么您将需要使用 StringBuilder。如果您只使用+=,那么您将构建一个全新的字符串每次调用+= 行。 StringBuilder 速度更快,因为它拥有一个缓冲区,并在您每次调用 append 时附加到该缓冲区。

【讨论】:

  • 是的,如果你要声明一个字符串文字,那么仅仅嵌入它显然是最快的方法。我有一个编程语言教授曾经告诉班级 Java 没有指针,所以你不能创建像链表这样的东西,他认为所有的 Java 集合都是用 C 或其他东西实现的。我让另一位讲师说“安全”网站是以“.shtml”结尾的网站(而不是以 https 开头)有时他们只是不知道自己在说什么
  • 另外,在一个紧密的循环中,您可以创建大量的字符串,每个字符串都比上一个大一点。在 C#(以及 .Net)中也是如此。我也同意 Eric Wendelin 的评论,即你应该写一个测试来看看。当您可以向您的教授展示他是对还是错的书面证据时,请不要告诉您的教授interwebz上人们的意见。我会做一些测试用例:通过每种方法创建一个字符串的速度,在循环中创建很多的速度等。
  • @jeffa00:我同意,仪器化测试是证明这一点的最佳方式。但是,请注意教授(无论如何以我的经验)不喜欢受到挑战。 特别是不要在课堂上这样做。
  • 如果您多次出现“123456”,那么它们都指的是同一个 intern()-ed 值。但是,如果您在多个“123456”上运行 String.format(),则会生成重复的副本。
【解决方案2】:

有点跑题了,但我希望整个“在 Java 中不能使用加连接字符串”的神话能够消失。虽然在 Java 的早期版本中 StringBuffer 更快并且“+ 是邪恶的”可能是真的,但在进行大量优化的现代 JVM 中肯定不是这样。

例如,哪个更快?

String s = "abc" + "def";

    StringBuffer buf = new StringBuffer();
    buf.append("abc");
    buf.append("def");
    String s = buf.toString();

答案是前者。 JVM 识别出这是一个字符串常量,实际上会将“abcdef”放入字符串池中,而“优化的字符串缓冲区”版本将导致构建额外的 StringBuffer 对象。

另一个 JVM 优化是

String s = onestring + " concat " + anotherstring;

JVM 将在哪里计算出最好的连接方式。在 JDK 5 中,这意味着 StringBuilder 将在内部使用,它会比使用字符串缓冲区更快。

但正如其他答案所说,您问题中的“123456”常数肯定是最快的方法,您的讲师应该回到学生身份:-)

是的,我已经很难过通过查看 Java 字节码来验证这一点......

【讨论】:

  • 更多 Gyaan 到您的“稍微偏离主题”的评论。在另一种情况下,使用 StringBuilder 创建字符串会更有效。如果您通过 for 循环连接不同的字符串,通常是这样的情况 String simple = ""; for (int counter = 0; counter
  • 糟糕,我刚刚意识到我在强调 part2 或 jasonh 的答案。
【解决方案3】:

整个讨论没有实际意义。请阅读 Jeff 的这篇文章,即创建 Stack Overflow 的人。

The Sad Tragedy of Micro-Optimization Theater

请让你的导师参考这篇文章,并要求他停止用无用的信息破坏他/她学生的大脑。算法优化是你的代码生死攸关的地方,而不是你使用什么方法来构造字符串。在任何情况下,StringBuilder 和 String 格式化程序都必须使用 REAL MEMORY 执行 ACTUAL CODE,如果您只是构造一个字符串,它会在编译期间被搁置一旁,并准备好在您需要时使用,本质上,它有 0 run-时间成本,而其他选项具有实际成本,因为实际上需要执行代码。

【讨论】:

  • 不要太挑剔,但我认为您的意思是说讨论“没有实际意义”。虽然它也是“静音”的,因为 SO 没有声音...... ;)
  • 让我们对这篇文章进行微优化! :)
  • 如果 OP 的程序太慢,并试图通过查看琐碎的字符串连接来优化它,我可能会同意你的观点,但这是一个 OP 想知道某些东西是如何工作的问题。对试图了解他们的编程平台的人说“在你需要它之​​前不要优化”就像说“在你的房子着火之前不要费心购买保险”。 (至于你帖子的其余部分,我完全同意。)
【解决方案4】:
String foo = "some string literal";

无疑是最快的制作字符串的方法。它嵌入在 .class 文件中,是一个简单的内存查找来检索。

当你没有什么要格式化的时候使用String.format看起来很丑,可能会让初级开发人员哭泣。

如果要修改字符串,那么StringBuilder 是最好的,因为Stringsimmutable

【讨论】:

  • 在没有什么要格式化的情况下使用 String.format 可能也会让高级开发人员哭泣。
【解决方案5】:

在您的第二个示例中,使用:

String foo = String.format("%s", 123456);

不会给你买任何东西; 123456 已经是一个常数值了,为什么不直接赋值 foo = "123456" 呢?对于常量字符串,没有更好的办法。

如果您要在运行时将多个部分附加在一起创建字符串,请使用 StringBuffer 或 StringBuilder(前者是线程安全的)。

【讨论】:

    【解决方案6】:

    如果您的字符串在编译时已知,那么最好使用文字:String foo = "123456";

    如果您的字符串在编译时未知并且由较小字符串的聚合组成,则StringBuilder 通常是要走的路(但要注意线程安全!)。

    使用String foo = String.format("%s", 123456); 可以减小 .class 的大小并加快类加载速度,但这将是非常激进(极端)的内存调整^^。

    【讨论】:

      【解决方案7】:

      正如已经指出的那样,如果您只是构建一个没有串联的单个字符串,只需使用 String。

      对于将多个位连接成一个大字符串,StringBuffer 比 StringBuilder 慢,但 StringBuffer 是同步的。如果你不需要同步,StringBuilder。

      【讨论】:

        【解决方案8】:

        您是否 100% 确定讲师没有在谈论以下内容:

        String foo = "" + 123456;
        

        我看到我的学生“一直”都在做这种事情(每个学期都会有少数人这样做)。他们这样做的原因是有些书向他们展示了如何这样做。对懒惰的作家摇摇头和拳头!

        【讨论】:

        • 我认为这是foo = String.valueOf(int_var) 的惯用快捷方式。我想创建(引用)空字符串有一些开销,但除此之外,它有什么问题?
        • 这不是最清楚的事情。 String.valueOf(int) 也调用 Integer.toString(int) (不是必需的,但这就是 JDK 所做的)。如果编译器这样做,那么它会比调用 Integer.toString(int) 慢,具体取决于环境。对我来说,Integer.toString(int) 比 "" + int 更清晰,也更明显。
        【解决方案9】:

        你给出的第一个例子是最快和最简单的。使用它。

        您在这些示例中添加的每段代码都会使其速度明显变慢且难以阅读。

        我建议示例 2 至少比示例 1 慢 10-100 倍,示例 3 比示例 2 慢大约 2 倍。

        您的处理器是否为此断言提供了任何理由?

        顺便说一句:你的第一个例子根本没有构造一个字符串(这就是它最快的原因),它只是给你一个位于字符串常量池中的字符串。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-01-12
          • 2021-05-08
          • 2012-02-12
          • 2018-02-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多