更新字符串的有效方法是什么?

时间:2016-06-30 22:21:35

标签: java string performance

假设我需要启动一个String然后再更新它几次,就像在这个伪代码中一样:

String s = "first";
if (<some condition>) { s += "second"; }
if (<some condition>) { s += "third"; }
if (<some condition>) { s += "fourth"; }

如果我最多有4次,那么以这种方式使用它还是只使用StringBuffer / StringBuilder会更好吗?

实际上我要问的是,如果String(这是一个可变类型)的更新时间是使用上述选项的有效方式吗?

4 个答案:

答案 0 :(得分:4)

首先,String不是一个可变类。每次使用+=时,都会返回另一个对象。

其次,除非你在紧密循环中使用它,否则无论如何都不是很重要。

第三,虽然它可能不是类似计算机科学的方法,但如果我尝试使用

    StringBuilder s = new StringBuilder("first");
    s.append( "second" );
    s.append( "third" );
    s.append( "fourth" );

Intellij建议用String替换它,并且只有在使用简单的String时至少同样有效才会出现该建议(尽管由于条件可能不完全相同)

根据Java: String concat vs StringBuilder - optimised, so what should I do? Java编译器确实用String构建器替换了一系列String连接:

public static void main(String[] args) {
    String s = "first";

    s+=" second" ;
    if(args.length >0)
    s+=" third";

    System.out.println("s, = " + s);
}

使用JDK 1.8.0_66并使用javap -c SbTest.class生成以下输出。虽然我不是字节代码的专家,但似乎创建了StringBuilder的多个实例(328)。因此,正如链接答案中所建议的那样,编译后的代码似乎是这样的:

    String s = "first";
    s = new StringBuilder().append(s).append(" second") ;
    if(args.length >0)
        s = new StringBuilder().append(s).append(" third") ;

因此,除非JIT对此进行优化,否则自己使用StringBuilder可能仍然更有效。

 public static void main(java.lang.String[]);
    Code:
       0: ldc           #2                  // String first
       2: astore_1
       3: new           #3                  // class java/lang/StringBuilder
       6: dup
       7: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
      10: aload_1
      11: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      14: ldc           #6                  // String  second
      16: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      19: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      22: astore_1
      23: aload_0
      24: arraylength
      25: ifle          48
      28: new           #3                  // class java/lang/StringBuilder
      31: dup
      32: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
      35: aload_1
      36: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      39: ldc           #8                  // String  third
      41: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      44: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      47: astore_1
      48: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
      51: new           #3                  // class java/lang/StringBuilder
      54: dup
      55: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
      58: ldc           #10                 // String s, =
      60: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      63: aload_1
      64: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      67: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      70: invokevirtual #11                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      73: return

答案 1 :(得分:2)

String s = "first";
if (<some condition>) { s += "second"; }
if (<some condition>) { s += "third"; }
if (<some condition>) { s += "fourth"; }

是正确的方法,因为编译器将turn it into a StringBuilder JDK 1.6及以上) 也就是说,除非您在循环中使用它,否则优化是相同的。

有关详细信息,请参阅此link

答案 2 :(得分:0)

如果你正在使用合理大小的字符串(比如你在那里的单词),使用字符串类就完全没问题了,使用字符串构建器类比使用普通字符串要快得多。当你可能处理大字符串(想想许多段落)并反复更改它们时,你想要替换它。 StringBuilder和StringBuffer应该大致相同,但StringBuffer是线程安全的,所以如果要编写多线程代码,那么使用StringBuffer。

答案 3 :(得分:-1)

要知道的唯一方法是进行基准测试。

package com.example;

import static org.junit.Assert.*;

import java.time.Duration;
import java.time.Instant;

import org.junit.Test;

public class ExampleTest {

    @Test
    public void test() {
        for (int j = 0; j < 4; j++) {
            Instant start = Instant.now();
            for (int i = 0; i < 10000; i++) {

                // Try various implementations here...
                String s = "first";
                if (true) {
                    s += "second";
                }
                if (true) {
                    s += "third";
                }
                if (true) {
                    s += "fourth";
                }
            }
            System.out.println("Took: " + Duration.between(start, Instant.now()).toMillis() + " millis");
        }
    }

}

如果表现不是问题,那么为人类进行优化并使其尽可能易读和可维护。