串接字符('x')与单个字符字符串文字(" x ")

时间:2022-01-19 21:36:40

When I have a String that I need to concatenate a single char to its end, should I prefer s = .... + ']' over s = .... + "]" for any performance reason?

当我有一个字符串,我需要将一个字符结束,我应该喜欢s = ....+ ']' / s = ....“为了任何性能原因?”

I know array string joining and of String builders, and I am NOT asking for suggestions on how to concatenate strings in general.

我知道数组字符串连接和字符串构建器,我并没有要求关于如何连接字符串的建议。

I also know some of would have the urge to explain to me about premature optimizations and that in general I should not bother with such minor stuff, please don't...

我也知道有些人会有向我解释过早优化的冲动,一般来说,我不应该为这些小事烦心,请不要……

I am asking because from a coding style preference I would prefer to use the later, but it feels to me that the first one should perform marginally better because knowing that what is being appended is just a single char there is no need for any internal looping going over this single char as there might be when copying a single character string.

我问,因为我宁愿从编码风格偏好使用之后,但在我的感觉,第一个应该执行略微更好,因为只知道什么是被附加一个字符不需要任何内部循环复习这一个char时可能会有复制一个字符的字符串。

Update

更新

As @Scheintod wrote this is indeed a theoretical Q and has to do more with my desire to better understand how java works and less with any real life "lets save another microsecond" scenario... maybe I should have said that more clearly.

正如@Scheintod所写的,这确实是一个理论问题,我需要做更多的事情,我希望更好地理解java的工作原理,更少地使用任何真实的生命“让我们保存另一微秒”的场景……也许我应该说得更清楚些。

I like understanding the way things work "behind the scenes" And I find that it can sometime help me create better code...

我喜欢理解事物在幕后工作的方式,我发现它有时可以帮助我创建更好的代码……

The truth - I was not thinking about compiler optimizations at all...

事实上,我根本没有考虑编译器的优化……

I would not have expected the JIT to use StringBuilders instead of Strings for me... Because I (possibly wrongly) think of String builders as being "heavier" then Strings on one hand but faster at building and modifying the strings on the other hand. So I would assume that in some cases using StringBuilders would be even less efficient then using stings... (if that was not the case then the entire String class should have had its implementation changed to be such as that of a StringBuilder and use some internal representation for actual immutable strings... - or is that what the JIT is sort of doing? - assuming that for the general case it would probably be better not to let the developer choose... )

我不希望JIT使用stringbuilder而不是字符串给我…因为我(可能是错误的)认为字符串构建器是“较重”的字符串,但是在构建和修改字符串时速度更快。所以我认为在某些情况下,使用StringBuilders会更低效,然后使用stings…(如果不是这样的话,那么整个String类就应该让它的实现变成像StringBuilder那样的实现,并对实际的不可变字符串使用一些内部表示……-或者说,JIT就是这样做的吗?-假设在一般情况下,最好不要让开发人员选择……

If it Does change my code to such a degree then maybe My Q should have been at that level asking if its is appropriate for the JIT to do something like this and would it be better if it used.

如果它确实改变了我的代码到这样的程度,那么我的Q应该在这个层次上问它是否适合JIT做这样的事情,如果它使用它会更好。

maybe I should start looking at compiled byte code... [I will need to learn how to do that in java ...]

也许我应该开始查看编译后的字节码…我需要学习如何在java中做到这一点……

As a side note and example of why I would even consider looking at bytecode - have a look at a quite old blog post of mine about Optimizing Actionscript 2.0 - a bytecode perspective - Part I it shows that knowing what your code compiles into indeed can help you write better code.

边注,甚至为什么我会考虑到字节码的例子,看一看我的一个很旧的博客优化Actionscript 2.0 -字节码的角度来说,我确实表明,知道你的代码编译成可以帮助你写出更好的代码。

3 个解决方案

#1


18  

Besides profiling this we have another possibility to get some insights. I want to focus on the possible speed differences and not on the things which remove them again.

除了剖析这一点,我们还有另一种可能获得一些见解。我想把注意力集中在可能的速度差异上,而不是把它们移走。

So lets start with this Test class:

那么让我们从这个测试类开始:

public class Test {

    // Do not optimize this
    public static volatile String A = "A String";

    public static void main( String [] args ) throws Exception {

        String a1 = A + "B";

        String a2 = A + 'B';

        a1.equals( a2 );

    }

}

I compiled this with javac Test.java (using javac -v: javac 1.7.0_55)

我用javac测试编译了这个。java(使用javac -v: javac 1.7.0_55)

Using javap -c Test.class we get:

使用javap - c测试。类我们得到:

Compiled from "Test.java"
public class Test {
  public static volatile java.lang.String A;

  public Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]) throws java.lang.Exception;
    Code:
       0: new           #2                  // class java/lang/StringBuilder
       3: dup
       4: invokespecial #3                  // Method java/lang/StringBuilder."<init>":()V
       7: getstatic     #4                  // Field A:Ljava/lang/String;
      10: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      13: ldc           #6                  // String B
      15: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      18: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      21: astore_1
      22: new           #2                  // class java/lang/StringBuilder
      25: dup
      26: invokespecial #3                  // Method java/lang/StringBuilder."<init>":()V
      29: getstatic     #4                  // Field A:Ljava/lang/String;
      32: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      35: bipush        66
      37: invokevirtual #8                  // Method java/lang/StringBuilder.append:(C)Ljava/lang/StringBuilder;
      40: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      43: astore_2
      44: aload_1
      45: aload_2
      46: invokevirtual #9                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z
      49: pop
      50: return

  static {};
    Code:
       0: ldc           #10                 // String A String
       2: putstatic     #4                  // Field A:Ljava/lang/String;
       5: return
}

We can see, that there are two StringBuilders involved (Lines 4, 22 ). So the first thing we discover is, that using + to concat Strings is effectively the same as using StringBuilder.

我们可以看到,有两个stringbuilder(第4行,第22行)。我们发现的第一件事是,使用+ to concat字符串实际上和使用StringBuilder是一样的。

The second thing we can see here is that the StringBuilders both are called twice. First for appending the volatile variable (Lines 10, 32) and the second time for appending the constant part (Lines 15, 37)

我们可以看到的第二件事是,StringBuilders都被调用了两次。第一个用于附加volatile变量(第10行、第32行)和第二次附加常量部分(第15、37行)

In case of A + "B" append is called with a Ljava/lang/String (a String) argument while in case of A + 'B' it is called with an C (a char) argument.

如果使用Ljava/lang/String (String)参数来调用A +“B”,则使用C (char)参数来调用。

So the compile does not convert String to char but leaves it as it is*.

因此,编译器不会将字符串转换为char,而是将其保留为*。

Now looking in AbstractStringBuilder which contains the methods used we have:

现在看看AbstractStringBuilder,它包含了我们使用的方法:

public AbstractStringBuilder append(char c) {
    ensureCapacityInternal(count + 1);
    value[count++] = c;
    return this;
}

and

public AbstractStringBuilder append(String str) {
    if (str == null) str = "null";
    int len = str.length();
    ensureCapacityInternal(count + len);
    str.getChars(0, len, value, count);
    count += len;
    return this;
}

as the methods actually called.

实际上是调用方法。

The most expensive operations here is certainly ensureCapacity but only in case the limit is reached (it does an array copy of the old StringBuffers char[] into a new one). So this is true for both and makes no real difference.

这里最昂贵的操作当然是ensurecacity,但只有在达到极限时(它会将旧的stringbuffer char[]的数组复制到一个新的)中。所以这两种情况都是成立的,没有什么区别。

As one can see there are numerous other operations which are done but the real distinction is between value[count++] = c; and str.getChars(0, len, value, count);

我们可以看到还有许多其他的操作,但真正的区别在于价值[count++] = c;和str.getChars(0, len, value, count);

If we look in to getChars we see, that it all boils down to one System.arrayCopy which is used here to copy the String to the Buffer's array plus some checks and additional method calls vs. one single array access.

如果我们看一下getChars,我们会看到,所有这些都归结为一个系统。这里使用的arrayCopy将字符串复制到缓冲区的数组中,加上一些检查和额外的方法调用vs.一个数组访问。

So I would say in theory using A + "B" is much slower than using A + 'B'.

所以在理论上,A + B比A + B要慢得多。

I think in real execution it is slower, too. But to determine this we need to benchmark.

我认为在实际执行过程中,它也比较慢。但要确定这一点,我们需要进行基准测试。

EDIT: Of cause this is all before the JIT does it's magic. See Stephen C's answer for that.

编辑:因为这都是在JIT之前的魔法。参见Stephen C的答案。

EDIT2: I've been looking at the bytecode which eclipse's compiler generated and it's nearly identical. So at least these two compilers don't differ in the outcome.

EDIT2:我一直在关注eclipse编译器生成的字节码,它几乎是相同的。所以至少这两个编译器在结果上没有区别。

EDIT2: AND NOW THE FUN PART

现在是有趣的部分。

The Benchmarks. This result is generated by running Loops 0..100M for a+'B' and a+"B" few times after a warmup:

基准。此结果由运行循环0生成。a+'B'和a+"B"在热身后几次:

a+"B": 5096 ms
a+'B': 4569 ms
a+'B': 4384 ms
a+"B": 5502 ms
a+"B": 5395 ms
a+'B': 4833 ms
a+'B': 4601 ms
a+"B": 5090 ms
a+"B": 4766 ms
a+'B': 4362 ms
a+'B': 4249 ms
a+"B": 5142 ms
a+"B": 5022 ms
a+'B': 4643 ms
a+'B': 5222 ms
a+"B": 5322 ms

averageing to:

平均:

a+'B': 4608ms
a+"B": 5167ms

So even in the real benchmark world of syntetic knowlege (hehe) a+'B' is about 10% faster than a+"B"...

因此,即使是在真正的标准知识世界中,a+ B的速度也比a+ B快10%……

... at least (disclaimer) on my system with my compiler and my cpu and it's really no difference / not noticeable in real world programms. Except of cause you have a piece of code you run realy often and all your application perfomance depends on that. But then you would probably do things different in the first place.

…至少(免责声明)在我的系统上有我的编译器和我的cpu,在现实世界的程序中没有明显的区别。除了因为你有一段代码你经常运行,你的所有应用程序性能都取决于这个。但首先你可能会做一些不同的事情。

EDIT4:

标:

Thinking about it. This is the loop used to benchmark:

思考这个问题。这是用于基准测试的循环:

    start = System.currentTimeMillis();
    for( int i=0; i<RUNS; i++ ){
        a1 = a + 'B';
    }
    end = System.currentTimeMillis();
    System.out.println( "a+'B': " + (end-start) + " ms" );

so we're really not only benchmarking the one thing we care about but although java loop performance, object creation perfomance and assignment to variables performance. So the real speed difference may be even a little bigger.

因此,我们不仅要对我们关心的一件事进行基准测试,而且还要考虑java循环性能、对象创建性能和变量性能的分配。所以真正的速度差异可能会更大。

#2


3  

When I have a String that I need to concatenate a single char to its end, should I prefer s = .... + ']' over s = .... + "]" for any performance reason?

当我有一个字符串,我需要将一个字符连接到它的结尾时,我是否应该选择s =+ ']' / s = ....“为了任何性能原因?”

There are actually two questions here:

这里有两个问题

Q1: Is there a performance difference?

Q1:有性能差异吗?

Answer: It depends ...

答:这取决于……

  • In some cases, possibly yes, depending on the JVM and/or the bytecode compiler. If the bytecode compiler generates a call to StringBuilder.append(char) rather than StringBuilder.append(String) then you would expect the former to be faster. But the JIT compiler could treat these methods as "intrinics" and optimize calls to append(String) with a one character (literal) string.

    在某些情况下,可能是的,取决于JVM和/或字节码编译器。如果字节码编译器生成一个对StringBuilder.append(char)的调用,而不是StringBuilder.append(String),那么您会期望前者更快。但是JIT编译器可以将这些方法视为“内在”,并优化调用以一个字符(文字)字符串附加(字符串)。

    In short, you would need to benchmark this on your platform to be sure.

    简而言之,您需要在您的平台上确定这一点。

  • In other cases, there is definitely no difference. For example, these two calls will be compiled identical bytecode sequences because the concatenation is a Constant Expression.

    在其他情况下,肯定没有区别。例如,这两个调用将被编译成相同的字节码序列,因为连接是一个常量表达式。

        System.out.println("234" + "]");
    
        System.out.println("234" + ']');
    

    This is guaranteed by the JLS.

    这是由JLS保证的。

Q2: Should you prefer one version over the other.

你是否应该选择一个版本而不是另一个版本。

Answer:

答:

  • In the general sense, this is likely to be a premature optimization. You should only prefer one form over the other for performance reasons if you have profiled your code at the application level and determined that the code snippet has a measurable impact on performance.

    在一般意义上,这可能是一个不成熟的优化。如果您在应用程序级别上对代码进行了分析,并确定代码片段对性能有可测量的影响,那么您应该只喜欢一种形式而不是另一种形式。

  • If you have profiled the code, then use the answer to Q1 as a guide.

    如果您已经对代码进行了分析,那么请使用Q1作为指南。

    And if it was worth trying to optimize the snippet, then is essential that you rerun your benchmarking / profiling after optimizing, to see if it made any difference. Your intuition about what is fastest... and what you have read in some old article on the internet ... could be very wrong.

    如果值得尝试优化代码片段,那么在优化后重新运行基准测试/概要分析,看看它是否有什么不同,这是非常重要的。你对什么是最快的直觉……你在网上看到的一些旧文章……可能是非常错误的。

#3


0  

No! That is premature optimization, and a complete waste of your time. Prefer whichever you prefer, and use whichever works. Sometimes ']' won't promote to "]" automatically. I expect the JIT will optimize away any performance difference you think you measure. And in the cases where it works, it works because ']' was converted to "]" (in your example). Now, using a StringBuilder directly and append(char) might be slightly faster... but again, this difference won't be meaningful in any real application.

不!这是不成熟的优化,完全浪费你的时间。你喜欢哪个就选哪个,用哪个都行。“有时”“不会”“自动”。我希望JIT能够优化你认为你衡量的任何性能差异。在它工作的情况下,它起作用是因为“]”被转换为“”)(在你的例子中)。现在,直接使用StringBuilder和append(char)可能会稍微快一些……但是,这种差异在任何实际应用中都不会有意义。

Edit

编辑

The best advice I can give you about performance is:

关于业绩,我能给你的最好建议是:

  1. Benchmark before you start making code changes (and try to limit yourself to areas where you are likely to have an impact through real critical-path identification) and
  2. 在开始进行代码更改之前(并尝试将自己限制在可能通过真正的关键路径标识进行影响的领域)和。
  3. If you can, stream a result instead of building a temporary String where possible (this will sometimes pay huge dividends with regards to memory usage and performance).
  4. 如果可以的话,不要在可能的情况下构建一个临时字符串(这会在内存使用和性能方面带来巨大的回报)。

#1


18  

Besides profiling this we have another possibility to get some insights. I want to focus on the possible speed differences and not on the things which remove them again.

除了剖析这一点,我们还有另一种可能获得一些见解。我想把注意力集中在可能的速度差异上,而不是把它们移走。

So lets start with this Test class:

那么让我们从这个测试类开始:

public class Test {

    // Do not optimize this
    public static volatile String A = "A String";

    public static void main( String [] args ) throws Exception {

        String a1 = A + "B";

        String a2 = A + 'B';

        a1.equals( a2 );

    }

}

I compiled this with javac Test.java (using javac -v: javac 1.7.0_55)

我用javac测试编译了这个。java(使用javac -v: javac 1.7.0_55)

Using javap -c Test.class we get:

使用javap - c测试。类我们得到:

Compiled from "Test.java"
public class Test {
  public static volatile java.lang.String A;

  public Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]) throws java.lang.Exception;
    Code:
       0: new           #2                  // class java/lang/StringBuilder
       3: dup
       4: invokespecial #3                  // Method java/lang/StringBuilder."<init>":()V
       7: getstatic     #4                  // Field A:Ljava/lang/String;
      10: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      13: ldc           #6                  // String B
      15: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      18: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      21: astore_1
      22: new           #2                  // class java/lang/StringBuilder
      25: dup
      26: invokespecial #3                  // Method java/lang/StringBuilder."<init>":()V
      29: getstatic     #4                  // Field A:Ljava/lang/String;
      32: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      35: bipush        66
      37: invokevirtual #8                  // Method java/lang/StringBuilder.append:(C)Ljava/lang/StringBuilder;
      40: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      43: astore_2
      44: aload_1
      45: aload_2
      46: invokevirtual #9                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z
      49: pop
      50: return

  static {};
    Code:
       0: ldc           #10                 // String A String
       2: putstatic     #4                  // Field A:Ljava/lang/String;
       5: return
}

We can see, that there are two StringBuilders involved (Lines 4, 22 ). So the first thing we discover is, that using + to concat Strings is effectively the same as using StringBuilder.

我们可以看到,有两个stringbuilder(第4行,第22行)。我们发现的第一件事是,使用+ to concat字符串实际上和使用StringBuilder是一样的。

The second thing we can see here is that the StringBuilders both are called twice. First for appending the volatile variable (Lines 10, 32) and the second time for appending the constant part (Lines 15, 37)

我们可以看到的第二件事是,StringBuilders都被调用了两次。第一个用于附加volatile变量(第10行、第32行)和第二次附加常量部分(第15、37行)

In case of A + "B" append is called with a Ljava/lang/String (a String) argument while in case of A + 'B' it is called with an C (a char) argument.

如果使用Ljava/lang/String (String)参数来调用A +“B”,则使用C (char)参数来调用。

So the compile does not convert String to char but leaves it as it is*.

因此,编译器不会将字符串转换为char,而是将其保留为*。

Now looking in AbstractStringBuilder which contains the methods used we have:

现在看看AbstractStringBuilder,它包含了我们使用的方法:

public AbstractStringBuilder append(char c) {
    ensureCapacityInternal(count + 1);
    value[count++] = c;
    return this;
}

and

public AbstractStringBuilder append(String str) {
    if (str == null) str = "null";
    int len = str.length();
    ensureCapacityInternal(count + len);
    str.getChars(0, len, value, count);
    count += len;
    return this;
}

as the methods actually called.

实际上是调用方法。

The most expensive operations here is certainly ensureCapacity but only in case the limit is reached (it does an array copy of the old StringBuffers char[] into a new one). So this is true for both and makes no real difference.

这里最昂贵的操作当然是ensurecacity,但只有在达到极限时(它会将旧的stringbuffer char[]的数组复制到一个新的)中。所以这两种情况都是成立的,没有什么区别。

As one can see there are numerous other operations which are done but the real distinction is between value[count++] = c; and str.getChars(0, len, value, count);

我们可以看到还有许多其他的操作,但真正的区别在于价值[count++] = c;和str.getChars(0, len, value, count);

If we look in to getChars we see, that it all boils down to one System.arrayCopy which is used here to copy the String to the Buffer's array plus some checks and additional method calls vs. one single array access.

如果我们看一下getChars,我们会看到,所有这些都归结为一个系统。这里使用的arrayCopy将字符串复制到缓冲区的数组中,加上一些检查和额外的方法调用vs.一个数组访问。

So I would say in theory using A + "B" is much slower than using A + 'B'.

所以在理论上,A + B比A + B要慢得多。

I think in real execution it is slower, too. But to determine this we need to benchmark.

我认为在实际执行过程中,它也比较慢。但要确定这一点,我们需要进行基准测试。

EDIT: Of cause this is all before the JIT does it's magic. See Stephen C's answer for that.

编辑:因为这都是在JIT之前的魔法。参见Stephen C的答案。

EDIT2: I've been looking at the bytecode which eclipse's compiler generated and it's nearly identical. So at least these two compilers don't differ in the outcome.

EDIT2:我一直在关注eclipse编译器生成的字节码,它几乎是相同的。所以至少这两个编译器在结果上没有区别。

EDIT2: AND NOW THE FUN PART

现在是有趣的部分。

The Benchmarks. This result is generated by running Loops 0..100M for a+'B' and a+"B" few times after a warmup:

基准。此结果由运行循环0生成。a+'B'和a+"B"在热身后几次:

a+"B": 5096 ms
a+'B': 4569 ms
a+'B': 4384 ms
a+"B": 5502 ms
a+"B": 5395 ms
a+'B': 4833 ms
a+'B': 4601 ms
a+"B": 5090 ms
a+"B": 4766 ms
a+'B': 4362 ms
a+'B': 4249 ms
a+"B": 5142 ms
a+"B": 5022 ms
a+'B': 4643 ms
a+'B': 5222 ms
a+"B": 5322 ms

averageing to:

平均:

a+'B': 4608ms
a+"B": 5167ms

So even in the real benchmark world of syntetic knowlege (hehe) a+'B' is about 10% faster than a+"B"...

因此,即使是在真正的标准知识世界中,a+ B的速度也比a+ B快10%……

... at least (disclaimer) on my system with my compiler and my cpu and it's really no difference / not noticeable in real world programms. Except of cause you have a piece of code you run realy often and all your application perfomance depends on that. But then you would probably do things different in the first place.

…至少(免责声明)在我的系统上有我的编译器和我的cpu,在现实世界的程序中没有明显的区别。除了因为你有一段代码你经常运行,你的所有应用程序性能都取决于这个。但首先你可能会做一些不同的事情。

EDIT4:

标:

Thinking about it. This is the loop used to benchmark:

思考这个问题。这是用于基准测试的循环:

    start = System.currentTimeMillis();
    for( int i=0; i<RUNS; i++ ){
        a1 = a + 'B';
    }
    end = System.currentTimeMillis();
    System.out.println( "a+'B': " + (end-start) + " ms" );

so we're really not only benchmarking the one thing we care about but although java loop performance, object creation perfomance and assignment to variables performance. So the real speed difference may be even a little bigger.

因此,我们不仅要对我们关心的一件事进行基准测试,而且还要考虑java循环性能、对象创建性能和变量性能的分配。所以真正的速度差异可能会更大。

#2


3  

When I have a String that I need to concatenate a single char to its end, should I prefer s = .... + ']' over s = .... + "]" for any performance reason?

当我有一个字符串,我需要将一个字符连接到它的结尾时,我是否应该选择s =+ ']' / s = ....“为了任何性能原因?”

There are actually two questions here:

这里有两个问题

Q1: Is there a performance difference?

Q1:有性能差异吗?

Answer: It depends ...

答:这取决于……

  • In some cases, possibly yes, depending on the JVM and/or the bytecode compiler. If the bytecode compiler generates a call to StringBuilder.append(char) rather than StringBuilder.append(String) then you would expect the former to be faster. But the JIT compiler could treat these methods as "intrinics" and optimize calls to append(String) with a one character (literal) string.

    在某些情况下,可能是的,取决于JVM和/或字节码编译器。如果字节码编译器生成一个对StringBuilder.append(char)的调用,而不是StringBuilder.append(String),那么您会期望前者更快。但是JIT编译器可以将这些方法视为“内在”,并优化调用以一个字符(文字)字符串附加(字符串)。

    In short, you would need to benchmark this on your platform to be sure.

    简而言之,您需要在您的平台上确定这一点。

  • In other cases, there is definitely no difference. For example, these two calls will be compiled identical bytecode sequences because the concatenation is a Constant Expression.

    在其他情况下,肯定没有区别。例如,这两个调用将被编译成相同的字节码序列,因为连接是一个常量表达式。

        System.out.println("234" + "]");
    
        System.out.println("234" + ']');
    

    This is guaranteed by the JLS.

    这是由JLS保证的。

Q2: Should you prefer one version over the other.

你是否应该选择一个版本而不是另一个版本。

Answer:

答:

  • In the general sense, this is likely to be a premature optimization. You should only prefer one form over the other for performance reasons if you have profiled your code at the application level and determined that the code snippet has a measurable impact on performance.

    在一般意义上,这可能是一个不成熟的优化。如果您在应用程序级别上对代码进行了分析,并确定代码片段对性能有可测量的影响,那么您应该只喜欢一种形式而不是另一种形式。

  • If you have profiled the code, then use the answer to Q1 as a guide.

    如果您已经对代码进行了分析,那么请使用Q1作为指南。

    And if it was worth trying to optimize the snippet, then is essential that you rerun your benchmarking / profiling after optimizing, to see if it made any difference. Your intuition about what is fastest... and what you have read in some old article on the internet ... could be very wrong.

    如果值得尝试优化代码片段,那么在优化后重新运行基准测试/概要分析,看看它是否有什么不同,这是非常重要的。你对什么是最快的直觉……你在网上看到的一些旧文章……可能是非常错误的。

#3


0  

No! That is premature optimization, and a complete waste of your time. Prefer whichever you prefer, and use whichever works. Sometimes ']' won't promote to "]" automatically. I expect the JIT will optimize away any performance difference you think you measure. And in the cases where it works, it works because ']' was converted to "]" (in your example). Now, using a StringBuilder directly and append(char) might be slightly faster... but again, this difference won't be meaningful in any real application.

不!这是不成熟的优化,完全浪费你的时间。你喜欢哪个就选哪个,用哪个都行。“有时”“不会”“自动”。我希望JIT能够优化你认为你衡量的任何性能差异。在它工作的情况下,它起作用是因为“]”被转换为“”)(在你的例子中)。现在,直接使用StringBuilder和append(char)可能会稍微快一些……但是,这种差异在任何实际应用中都不会有意义。

Edit

编辑

The best advice I can give you about performance is:

关于业绩,我能给你的最好建议是:

  1. Benchmark before you start making code changes (and try to limit yourself to areas where you are likely to have an impact through real critical-path identification) and
  2. 在开始进行代码更改之前(并尝试将自己限制在可能通过真正的关键路径标识进行影响的领域)和。
  3. If you can, stream a result instead of building a temporary String where possible (this will sometimes pay huge dividends with regards to memory usage and performance).
  4. 如果可以的话,不要在可能的情况下构建一个临时字符串(这会在内存使用和性能方面带来巨大的回报)。