Distinguish between String+String and String+char in Java

  • 2020-04-01 04:37:27
  • OfStack

Let's consider a question about String in Java: the difference between "ABC" + '/' and "ABC" + "/".

What's the difference between using a slash/as a character or as a string?
One is treated as a char of the basic data type and one as an object String. What's the difference?
Is it more efficient as a character?
String STR = "ABC" + '/';
and
String STR = "ABC" + "/";

Compiler optimization

First of all, you should know that the above two sentences are the same effect, because the compiler will optimize the above two sentences to the following:


String str = "abc/";

We can prove this with javap, a reference to the 5 JDK tools every Java developer should know.


StringOne.java
String str1 = "abc" + '/';
String str2 = "abc" + "/";
System.out.println(str1 == str2);

Compile and run, and the output is true. Next, it's time for our javap to enter the following command from the command line:


javap -v -l StringOne.class > StringOne.s

Then look at the generated stringone. s file and you'll see that there are a few lines


StringOne.s
#2 = String       #20      // abc/
...
#20 = Utf8        abc/
...
0: ldc      #2         // String abc/
2: astore_1
3: ldc      #2         // String abc/
5: astore_2

Indicates that both str1 and str2 refer to the string "ABC \".

2. Use javap to analyze differences

Now let's change the question, what's the difference between the stringAddString and stringAddChar methods in the following code ?


StringTwo
public static String stringAddString(String str1, String str2){
  return str1 + str2;
}

public static String stringAddChar(String str, char ch){
  return str + ch;
}

This time, decompile using javap, and the generated file portion is shown below


StringTwo.s
public java.lang.String stringAddString(java.lang.String, java.lang.String);
 descriptor: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
 flags: ACC_PUBLIC
 Code:
  stack=2, locals=3, args_size=3
    0: new      #2         // class java/lang/StringBuilder
    3: dup
    4: invokespecial #3         // Method java/lang/StringBuilder."< init>":()V
    7: aload_1
    8: invokevirtual #4         // Method java/lang/StringBuilder. append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   11: aload_2
   12: invokevirtual #4         // Method java/lang/StringBuilder. append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   15: invokevirtual #5         // Method java/lang/StringBuilder. toString:()Ljava/lang/String;
   18: areturn

public java.lang.String stringAddChar(java.lang.String, char);
 descriptor: (Ljava/lang/String;C)Ljava/lang/String;
 flags: ACC_PUBLIC
 Code:
  stack=2, locals=3, args_size=3
    0: new      #2         // class java/lang/StringBuilder
    3: dup
    4: invokespecial #3         // Method java/lang/StringBuilder."<init>":()V
    7: aload_1
    8: invokevirtual #4         // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   11: iload_2
   12: invokevirtual #6         // Method java/lang/StringBuilder.append:(C)Ljava/lang/StringBuilder;
   15: invokevirtual #5         // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   18: areturn

Now, we can clearly see the execution flow of these two methods:

stringAddString

Create a StringBuilder object Using the append method, add the two arguments in turn to the StringBuilder you just created. Call the toString method. The return value of the return toString method.

The procedure for stringAddChar is the same as for stringAddString, except that when the append method is called the second time, stringAddString takes the parameter of type String, and stringAddChar takes the parameter of type char.

3. Append (char) method and append(String) method of StringBuilder class

Here, we directly look at the source code (mine is jdk1.8.0_60 with the source code). Note that although the document shows that StringBuilder inherits from Object, from the source code point of view, it inherits from the abstract class AbstractStringBuilder. And the append method is implemented by AbstractStringBuilder.

AbstractStringBuilder. Java


public AbstractStringBuilder append(char c) {
  ensureCapacityInternal(count + 1);  //Make sure the array can hold count+1 characters
  value[count++] = c;
  return this;
}

public AbstractStringBuilder append(String str) {
  if (str == null)
    return appendNull();
  int len = str.length();
  ensureCapacityInternal(count + len);
  str.getChars(0, len, value, count); //Copies the array of characters in the string into the array of characters in this object
  count += len;
  return this;
}

The rest is not posted anymore. String.getchars (int, int, char[], int) finally depends on public static native void arraycopy(Object, int, Object, int, int). That said, it's probably written in C, and it should be more efficient at copying large arrays than programs written in Java. So, here's what I understand:

From direct memory for, because the String contained in the char array, the array should be have a length field, at the same time the String class has a int hash properties, combined with the object itself takes up extra memory store other information, so the String will occupy more memory. But if the String is very long, so the memory overhead is almost can be ignored; If, as in the case of "/", the string is (very) short, then there are likely to be many Shared references to share the memory overhead, and the extra memory overhead can be ignored.

From the call stack, since String is only one or two more function calls than char, if you don't consider the overhead of function calls (both in time and space), it should be about the same. Considering the overhead of function calls, "ABC" + '/' is better. But when you need to concatenate several characters (which seems to be more common ?) Since using char requires a lot of looping to complete the connection, the number of calls to the function will only be more than using String.
Now it feels like the question is asking: Read and write files to use system calls with high efficiency, or use the standard IO library in the library of high efficiency. Personal feeling, although the standard IO library finally still have to call the system call, and this can produce some temporary between variables, and the deeper the call stack, but the IO library buffer mechanism, so the IO library throughput will be bigger, and the real-time performance of the system call will be better. Similarly, although the String class will be a few more fields, have deeper function stack, but due to the cache and more direct copy, should be able to better throughput.

The new problem

From the decompilation code in javap above, the two strings add up to the append String in StringBuilder.


String str1 = "abc" + "123";  // 1

StringBuilder stringBuilder = new StringBuilder(); // 2
stringBuilder.append("abc");
stringBuilder.append("123");
String str2 = stringBuilder.toString();

I'll leave that to you to think about.

The above is all the content of this article, to help you better distinguish between Java String+String and String+char, I hope to help you learn.


Related articles: