22

when I use the IDE -"IDEA 14.03", it always give this notice for me. notice: 'StringBuilder sb' can be replaced with 'String'

Here is the details, when I define a object named "sb",and the object class is "StringBuilder". Here is code snippet that I tried:

StringBuilder sb = new StringBuilder("status=").append(status).append(" ,msg=").append(msg);
System.out.println(sb);

I just want to know what are the benefit if I change the "StringBuilder" to "String". And why the IDE always notify me to change the class type?

Blasanka
  • 21,001
  • 12
  • 102
  • 104
aworker
  • 341
  • 1
  • 2
  • 6
  • 4
    It says that you can replace string builder code with string concatenation (`String r = s1 + s2;`) without it affecting your code at all, since you have no conditionals while building your strings. – M. Prokhorov Mar 17 '17 at 11:16
  • thanks for your answer, I still have the puzzle whether there is any benefit if I take the idea given by the IDEA? Or it just the same effect of the codes I write. – aworker Mar 17 '17 at 11:30
  • If you have no conditionals during concatenation, there will be no difference. If you have, then string builder is slightly better, but overall this will also likely not matter all that much. – M. Prokhorov Mar 17 '17 at 11:32
  • 1
    I wonder why you ignore the explanation added by IDEA. It tells you why it suggests this change. – Tom Mar 17 '17 at 13:32

5 Answers5

33

I think in order to understand why your IDE tells you to change StringBuilder to String, you should understand the differences between String, StringBuffer and StringBuilder.

String is immutable. That means if you want to change something from the your string, the original string will not be deleted but created a new one, which includes your changes. StringBuffer and StringBuilder are mutable. That means with your changes, the original string will be changed accordingly.

The another main difference between them is that String and StringBuffer are thread-safe while StringBuilder is not. There are also other differences, please have a look at this site to learn more about the differences.

If you compare String with StringBuilder, on most cases, using String is more practical and logical, if you do not know, what you do with your string.

It is not always better to concatenate string with plus sign (+). For example, StringBuilder's append method is more logical if you change your string in a loop because of its mutability. Please read the comments in the code;

String a;
StringBuilder b;
for(int i=0; i<5; i++)
{
a += i; //String is immutable and in each iteration, a new object will be created
b.append(i); //StringBuilder is mutable and in each iteration, the existing string will be used.
}

What your IDE makes is just show you the best practices. That is why, it is called as recommendation.

If you want to go on your way anyway and do not want Intellij warn you about it; you can disable the warning like;

enter image description here

EDIT

@CrazyCoder's comment is important to note here.

IDE is actually very smart here, it suggests you to change it for better code readability since internally compiler will generate exactly the same bytecode and your code will have the same performance and the same memory usage, but it will be easier to read. You get a readability benefit without any performance compromises. Similar question was asked and answered in IntelliJ IDEA forum some time ago.

Ad Infinitum
  • 3,560
  • 27
  • 33
  • 3
    You haven't explained _why_ IDEA suggests to use String concatenation (your answer only assumes that this would result in additional 4 substrings which are unneded and only mean more memory) rather then using StringBuilder manually. – Tom Mar 17 '17 at 13:01
  • 1
    @Tom I do not want to spoon-feed the OP. I have tried to show the way, he can reach his answer and if he wants to disable this option after his research, i have shown, how he can disable it. I think this answer answes both of his questions. However, I will add some more links, which ease his learn-process. – Ad Infinitum Mar 17 '17 at 13:07
  • 2
    Ok, then call your incomplete answer "not spoon-feed OP", whatever. But I wonder why you've added unrelated information like im-/mutability or thread-safety, although it is obivously not the reason why IDEA suggests the change to String concatenation. – Tom Mar 17 '17 at 13:21
  • I have added more info to the answer. immutability or thread-safety is not unrelated to the question because the IDEA makes his decision based on these differences. – Ad Infinitum Mar 17 '17 at 13:25
  • IT DOESN'T MATTER. The compiler compiles the String concatenation into the usage of StringBuilder so it doesn't make sense to do that manually in the manner like OP did, that just clutters the code and reduces readability (IDEA explains that btw in the warning). Neither Immutability, not thread-safety is important. You're right about loops, but OP didn't ask about that. – Tom Mar 17 '17 at 13:29
  • Please read the question carefully. The question consists of three subquestions. The first question is; **How to prevent “notice: 'StringBuilder sb' can be replaced with 'String'” in intellij-idea?** My answer answers this question. And the second question is; **What are the benefit if I change the "StringBuilder" to "String"?** My answer answers this question as well and the last question is; **Why the IDE always notify me to change the class type?** and this is also answered. – Ad Infinitum Mar 17 '17 at 13:35
  • I understand your point. You claim that this answer does not answer OP's question and/or this answer has more information than it need to have. With the second one, i would agree with you but i have made it intentionally because he should learn the real cause of the IDEA's suggesstion instead of superficial cause. – Ad Infinitum Mar 17 '17 at 13:48
  • *"the real cause of the IDEA's suggesstion instead of superficial cause."* This makes me wonder if you realize _when_ IDEA suggests that. I hope you'll then understand that threading and immutability are unrelated in these situations. Your reasons can be true for other cases or about String and StringBuilder in general, but this is not the scope of this question. – Tom Mar 17 '17 at 14:25
  • threading and immutability part of the answer are not for the IDEA part of the question. It is for the question **What are the benefit if I change the "StringBuilder" to "String"?**. As I said before, there are 3 subquestions in the question above and you think still that there is just one question, which is **Why the IDE always notify me to change the class type? ** – Ad Infinitum Mar 17 '17 at 14:29
  • *"As I said before, there are 3 subquestions"* Because you keep ignoring the context. But discussion that even further won't help either. – Tom Mar 17 '17 at 15:20
  • Both of you gave me the very important know-how. But please forgive me for do not get the point why IDE recommend me to change the class type. I don't see any benefit in my case to make the change. What I know is that it will be a memory wastage to change the class type to String from Ad's answer. What a foolish IDE! – aworker Mar 17 '17 at 16:04
  • 5
    @aworker IDE is actually very smart here, it suggests you to change it for better code readability since internally compiler will generate exactly the same bytecode and your code will have the same performance and the same memory usage, but it will be easier to read. You get a readability benefit without any performance compromises. Similar question was [asked and answered in IntelliJ IDEA forum](https://intellij-support.jetbrains.com/hc/en-us/community/posts/115000093250-Convert-StringBuilder-to-String-concatenation-suggestion) some time ago. – CrazyCoder Mar 18 '17 at 01:21
  • @CrazyCoder I got what you say. – aworker Mar 19 '17 at 12:36
8

I know it's an old question that was already answered with a very good answer, but just a tiny unrelated comment: In such cases, for the sake of readability, (and since the compiler does the same anyway as Ad mentioned) I would use String.format. instead of

StringBuilder sb = new StringBuilder("status=").append(status).append(" ,msg=").append(msg);

I find this more readable:

String.format("status=%s, msg=%s", status, msg);
Udi
  • 1,080
  • 1
  • 12
  • 25
4

Because code 1

String s1 = "a";
String s2 = "b";
String result = s1 + s2;

With code 2

String s1 = "a";
String s2 = "b";
String result = new StringBuilder().append(s1).append(s2).toString();

After being compiled into bytecode, the results of the two pieces of code are the same. code 1 will be optimized to StringBuilder in bytecode by the compiler:

   L0
    LINENUMBER 15 L0
    LDC "a"
    ASTORE 1
   L1
    LINENUMBER 16 L1
    LDC "b"
    ASTORE 2
   L2
    LINENUMBER 17 L2
    NEW java/lang/StringBuilder
    DUP
    INVOKESPECIAL java/lang/StringBuilder.<init> ()V
    ALOAD 1
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    ALOAD 2
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
    ASTORE 3
   L3
    LINENUMBER 18 L3
    RETURN
   L4
    LOCALVARIABLE args [Ljava/lang/String; L0 L4 0
    LOCALVARIABLE s1 Ljava/lang/String; L1 L4 1
    LOCALVARIABLE s2 Ljava/lang/String; L2 L4 2
    LOCALVARIABLE result Ljava/lang/String; L3 L4 3
    MAXSTACK = 2
    MAXLOCALS = 4

From the perspective of bytecode, IDAE believes that these two writing methods are equivalent, and the writing method of code 1 is more concise. So it is recommended to use code 1

ps: If you don't like it, you can turn off this prompt :)

The above test is based on jdk1.8

zw1one
  • 41
  • 2
3

Although the code is easier to read with String concatenation, for Java 8 and below, this is implemented with a StringBuilder, so on the surface, it looks like your just hurting yourself.

However, StringBuilder has a default capacity of 16. If you dig in the source code for StringBuilder you'll see that the realloc uses:

         int newCapacity = (value.length << 1) + 2;

So, basically you double every time. So, for arbitrary string of length, say 100, you'll end up allocating space and copying 4 times with capacities of 16, 32, 64, and finally 128.

However, you could do something like:

new StringBuffer(128).sb.append("status=").append(status).append(" ,msg=").append(msg).toString();

And, you've saved yourself 3 allocs and array copies.

As I understand it, Java 9+ has a much better solution. So, Knowing that this will be fixed, I usually use Strings unless I know performance is a concern and then I revert StringBuilder's. This is definitely the case if you're working on embedded systems or Android as resources are scarce. On a cloud server, not so much.

As for the IntelliJ inspector, I turned it off, but I usually add a JAVA10 tag in a comment, so that I can find these later and revert them when we migrate to 10. Maybe, I just need to remember to re-enable the inspector instead. :)

PS- I like Udi's answer. I'll look into the implementation.

Kurt
  • 399
  • 3
  • 14
  • I did look at the String.formatter code in Java 8, and it's backed by a StringBuilder and it's not shy about allocating resources, so I suspect it suffers from similar issues. Too bad. I'll have to check Java 10 when I get a chance. – Kurt Jul 24 '18 at 19:04
0

And why the IDE always notify me to change the class type

java 8: the same effect of the codes, but string concatenation

String r = s1 + s2;

is more readable

Michael Lihs
  • 7,460
  • 17
  • 52
  • 85