在Java中使用String或StringBuffer:哪个更好?

5

我读了很多关于在Java中使用StringBuffer和String,特别是涉及到连接字符串时,哪一个是线程安全的。

那么,在各种Java方法中,应该使用哪种呢?

例如,在PreparedStatement中,查询语句应该使用StringBuffer:

    String query = ("SELECT * " +
                    "FROM User " +
                    "WHERE userName = ?;");

    try {
        ps = connection.prepareStatement(query);

再看字符串工具方法,比如:

public static String prefixApostrophesWithBackslash(String stringIn) {
    String stringOut = stringIn.replaceAll("'", "\\\\'");
    return stringOut;
}

并且:

 // Removes a char from a String.
public static String removeChar(String stringIn, char c) {
    String stringOut = ("");
    for (int i = 0; i < stringIn.length(); i++) {
        if (stringIn.charAt(i) != c) {
            stringOut += stringIn.charAt(i);
        }
    }
    return stringOut;
}

我应该使用StringBuffer吗?特别是在这些对象中repalceAll不可用的情况下。

谢谢

Morgan先生。

感谢您提供的所有建议。我已经将StringBuffer替换为StringBuilder,并在我认为最好的地方将String替换为StringBuilder。

6个回答

7

您几乎不需要使用 StringBuffer

您可能想要使用的是 StringBuilder。StringBuffer 类似于 StringBuilder,但它还提供了线程安全性。实际上很少需要这种线程安全性,并且只会使您的代码运行更慢。

您的问题似乎不是关于 String vs StringBuffer,而是关于使用内置方法还是自己实现代码。如果有一个内置方法可以完全满足您的需求,那么您应该使用它。很可能它比您编写的代码优化得更好。


1
我希望在第一个示例中不要使用内部的StringBuilder,而是编译器可以将字符串连接成一个字符串,因为没有变化。 - Thomas Lötzer
但是考虑到这个PreparedStatement可能在多线程场景中使用,StringBuffer会不会更好一些呢? - Mr Morgan
4
@Mr Morgan - 您将使用StringBuilder作为构建查询字符串的中间步骤,这个局部变量不会被其他线程看到。因此,它不会存在并发问题-不要简单地假设“多线程应用程序=>在任何地方都使用线程安全类”。 - Andrzej Doyle

3
除了反复使用 "StringBuilder" 与 "StringBuffer" 这个口号之外,没有简单的答案...。为了选择最有效的解决方案,您确实需要了解一些底层信息。
在您的第一个示例中,应该使用 "String"。Java编译器可以为由字符串连接序列组成的任何表达式生成几乎最佳的代码(如果必要,使用 "StringBuilder")。如果连接的字符串都是常量或字面值,则编译器实际上可以在编译时完成连接。
在您的第二个示例中,不太清楚使用 "String" 还是 "StringBuilder" 更好...或者它们是否大致等效。需要查看 "java.util.regex.Matcher" 类的代码才能弄清楚。
编辑 - 我查看了代码,实际上使用 "String" 或 "StringBuilder" 作为源没有太大区别。在内部,"Matcher.replaceAll" 方法创建一个新的 StringBuilder,并通过从源字符串和替换字符串添加块来填充它。
在您的第三个示例中,显然最好使用 "StringBuilder"。当前一代 Java 编译器无法优化代码(如所写)以避免在添加每个字符时创建新的字符串。

1

对于下面的代码段

 // Removes a char from a String.
public static String removeChar(String stringIn, char c) {
    String stringOut = ("");
    for (int i = 0; i < stringIn.length(); i++) {
        if (stringIn.charAt(i) != c) {
            stringOut += stringIn.charAt(i);
        }
    }
    return stringOut;
}

你可以直接使用 stringIn.replaceAll(c+"","")

1
现代编译器已经对代码进行了优化。因此,一些字符串添加操作将被优化为使用 StringBuilder,并且如果我们认为它增加了可读性,我们可以保留字符串添加操作。 示例1:
String query = ("SELECT * " +
                "FROM User " +
                "WHERE userName = ?;");

将被优化为类似于:

StringBuiler sb = new StringBuilder();
sb.append("SELECT * ");
sb.append("FROM User ");
sb.append("WHERE userName = ?;");
String query = sb.toString();

示例2:

String numbers = "";
for (int i = 0;i < 20; i++)
  numbers = numbers + i;

这个无法被优化,我们应该在代码中使用 StringBuilder。


我是基于SUN jdk1.5+做出这个观察的。因此,对于旧版本的Java或不同的jdk,情况可能会有所不同。在那种情况下,始终编写StringBuilder(或针对jdk 1.4.2及更早版本的StringBuffer)可能是安全的选择。


2
实际上,当连接字符串常量时,编译器会将其优化为单个字符串常量。 - ILMTitan

1
即使在MT代码中,多个线程追加字符串也是不寻常的。StringBuilder几乎总是优先于StringBuffer。

0

对于可以视为单线程的情况,最好使用StringBuilder。它不会增加任何同步开销,而StringBuffer会。

使用'+'运算符进行字符串连接仅在您懒得使用StringBuilder或只想保持代码易读性并且从性能角度来看是可接受的时候才是“好”的,比如在启动日志消息中:“LOG.info("Starting instance " + inst_id + " of " + app_name);"


实际上,在绝大多数情况下,编译器会将第二种形式转换为一系列 StringBuilder 调用。因此,通常情况下,对于你在示例中提供的类型的事情,使用字符串连接 更好 ,因为它更清晰简洁。如果你不是直接连接的话,例如在循环中或者在函数调用之间传递生成器,那么你可以使用 StringBuilder。 - Andrzej Doyle
@Andrzej Doyle - 实际上,在我的示例中,我建议使用字符串连接。请在评论之前仔细阅读。 - bobah
是的,但你的第二段暗示了性能较差。我的观点是,在大多数情况下(其中连接操作在词法上是静态的),编译器将无论如何将其转换为StringBuilder等效字节码。因此,你可以同时拥有并享用它,这不是我感到的印象。 - Andrzej Doyle

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