String.format()与"+"运算符的区别

33

基本字符串连接操作应该使用什么?

String randomString = "hello " + userName + " how are you" ?
或者
String randomString = String.format("hello %s how are you ?",userName);

我认为String.format()能更好地输出字符串。 但是,使用其中任何一个的优缺点是什么?

在性能或字符串文字池中是否有孤儿条目方面有什么问题吗?

编辑:我谈论的是字符串中的多个参数,而不仅仅是一个。约为5+。

奖励问题:请分享您对应该使用哪个方法的看法?任何一个还是第三个...!?


3
可能是与https://dev59.com/CnNA5IYBdhLWcg3whuZ_重复的内容。 - mtk
@mtk 那个问题的答案中一个有趣的点是本地化的概念。使用字符串国际化,您可以在不重新编译的情况下将 String.format 的第一个参数替换为具有不同参数顺序或措辞的参数。这是 String.format 的一个巨大优势。 - Brian
9个回答

32

如果你只关注性能,我认为使用 StringBuilder/StringBuffer 是构建字符串最高效的方式。即使Java编译器聪明到能够将大多数字符串连接转换为 StringBuilder 的等效形式。

如果你更注重代码可读性,我认为使用 String.format 更清晰易懂,这也是我在不需要高性能的情况下使用的方法。

所以,如果你的主要关注点不是性能,也就是说这段代码并不被频繁调用,那么你可能更喜欢使用 String.format ,因为它可以更好地展示结果字符串(正如你所说)。

此外,使用 String.format 让你可以使用格式化功能,这意味着你可以用它来填充字符串、格式化数字、日期等等,如果使用简单拼接,则会使代码变得更糟糕。

编辑 Chuu

使用JAD,你可以看到以下代码:

public class Test {
    public static void main(String[] args) {
        String str = "a" + "b" + "c";
        String str2 = "foo" + str + "bar" + str;
        System.out.println(str2);
    }
}

反编译后的代码如下:

public class Test {
    public static void main(String[] args) {
        String str = "abc";
        String str2 = new StringBuilder("foo").append(str).append("bar").append(str).toString();
        System.out.println(str2);
    }
}

通过使用 javap 工具可以证明,在 .class 文件下,它将显示 Java 字节码:

public static void main(java.lang.String[] args);
    0  ldc <String "abc"> [16]
    2  astore_1 [str]
    3  new java.lang.StringBuilder [18]
    6  dup
    7  ldc <String "foo"> [20]
    9  invokespecial java.lang.StringBuilder(java.lang.String) [22]
   12  aload_1 [str]
   13  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [25]
   16  ldc <String "bar"> [29]
   18  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [25]
   21  aload_1 [str]
   22  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [25]
   25  invokevirtual java.lang.StringBuilder.toString() : java.lang.String [31]
   28  astore_2 [str2]
   29  getstatic java.lang.System.out : java.io.PrintStream [35]
   32  aload_2 [str2]
   33  invokevirtual java.io.PrintStream.println(java.lang.String) : void [41]
   36  return

我不知道Android的Java实现的具体细节,但在大多数动态语言中,“+”运算符始终是连接字符串最快的方式。这是因为大多数JIT编译器会将其转换为计算结果字符串的大小,执行一次内存分配,然后将所有内容复制到它分配的内存中。String.format和StringBuffer都需要多个动态分配和/或创建临时对象进行收集。 - Chuu
1
Java 编译器会自动将 "a" + "b" + "c" 翻译成 "abc",但如果涉及变量,则在可能的情况下使用 StringBuilder。因此,String str1 = "a"; String str2 = "a" + str1; 将被翻译成 String str2 = new StringBuilder("a").append(str1)).toString();。可以通过像 JAD 这样的 Java 反编译器验证此行为。 - Alex
1
@Chuu 请参考上面的编辑。 - Alex
@Alex:Chuu的说法似乎是关于JIT编译器的。它们在运行时和加载时进行优化,这在类文件中不可见。 - Lii
@Alex,你在反编译的文件中是否意味着String str = "ac";而非String str = "abc"; - Vikas Prasad
@VikasPrasad 当然,发现得好 :) - Alex

8

试试这个

    long t0 = System.currentTimeMillis();
        String userName = "test";
        for (int i = 0; i < 1000000; i++) {
            String randomString = "hello " + userName + " how are you?";
//          String randomString = String.format("hello %s how are you ?",userName);
        }
        System.out.println(System.currentTimeMillis() - t0);

您会惊讶地发现,串联字符串(concatenation)比String.format快10倍。但是format可以对数字、日期等进行许多极其有用的操作。有关详细信息,请参见java.util.Formatter API,该API实际上被String.format使用。


2
+1 用于性能测试 - DerMike
由于编译器是使用 + 运算符连接 StringBuilder 实现的,因此它会更快。但是没错!就字符串连接而言,String.format 会慢得多。 - arunken

8
基本的字符串拼接操作应该使用什么?
你提供的示例有不同的用途。 "+" 被重载为连接 Strings 但是,String.format被用于格式化字符串,正如其名称所示。
将字符串连接在一起并不是它的主要工作。
因此,如果要求仅仅是连接,可以使用 '+' 或者 concat 方法。
这些链接可能会有所帮助: 如果性能很重要,我应该使用Java的String.format()吗? 在Java中使用String.format是否比字符串连接更好的做法?

但我经常看到使用String.format()进行连接而不是实际上的“格式化”字符串。 - Priyank Doshi
3
你不应该这样做。但如果需要格式化,建议使用“format”。 - Azodious

4

在我看来,应该使用+运算符

如果性能是一个问题,你应该使用StringBuilder,正如Alex在他上面的评论中指出的那样,Java编译器会将:

String str2 = "a" + str1;

转换为:String str2 = new StringBuilder("a").append(str1)).toString();

此外,你可以通过使用+运算符避免运行时异常。相反,你会得到在编译时就可以捕获的语法错误。例如:

    String var = "huge success.";

    System.out.print( "I'm making a note here: " + var );
    //System.out.print( "Printing: " + ); Syntax error!

    System.out.print( String.format( "I'm making a note here: '%s'", var ) );
    System.out.print( String.format( "I'm making a note here: '%s'" ) ); // runtime exception!
    System.out.print( String.format( "I'm making a note here: '%d'", var ) ); // runtime exception!

易读性是个人偏好问题。然而,我会说String.format语法基本上来自于C++,自那时以来大多数语言为了易读性都采用了+操作符


2

我的建议:始终使用 String.format()

国际化比样式或性能更重要。因此,在这种情况下,我会始终使用 String.format()

英文文本:"Hello " + userName + " how are you?"

Jedi 翻译:userName + "你怎么样?你好!"

如果字符串被连接起来,变更单词顺序可能对翻译者来说难以实现,甚至不可能实现。如果字符串有更多的片段和占位符,则这个问题会变得越来越严重。


1

个人而言,我更喜欢使用格式化,因为它看起来更好,而且我无法想象它会对性能产生重大影响。


1
大多数推荐使用StringBuffer的人都停留在过去。当前最佳实践是使用StringBuilder。但这可能会改变,为什么不让编译器为您完成呢?当您只使用“+”时,编译器将选择最佳的当前实现,甚至可以组合静态字符串(“foo”+“bar”将成为单个“foobar”)。
唯一需要显式分配StringBuilder的地方是在循环中(在循环之前创建并在其中使用)。
如果您对发生的事情有疑问,请检查生成的字节码。
那么何时使用格式?当实际格式化复杂时,我会使用格式。但是您现在正在谈论简单的连接。

1

我认为String.Format更加高效,因为它具有以下优点:

  1. 提高可读性
  2. 更易于翻译
  3. 您可以在格式字符串中使用格式提供程序和简单的格式指示符(如固定宽度)。
  4. 通过将格式字符串放入配置文件中,您可以执行一些强大的操作。
  5. String.Format()通常更快,因为它在幕后使用StringBuilder和高效的状态机,而在.Net中进行字符串连接相对较慢。特别是当字符串大小和替换值的数量增加时,这一点尤为明显。

谢谢。


0

我对 String.format 的主要顾虑是它是一个与语言环境相关的操作,如果您的代码在不同的语言环境下运行,可能会得到无法预测的结果。

我希望有一个非语言环境的替代方案,因为其紧凑的语法非常好用。


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