2
String s1 = "Java";
String s2 = "Java";
StringBuilder sb1 = new StringBuilder();

sb1.append("Java");

System.out.println(s1 == s2);                  //true
System.out.println(s1.equals(s2));             //true
System.out.println(sb1.toString() == s1);      //false
System.out.println(sb1.toString().equals(s1)); //true

Why is the third print statement printing false? How to make it print true?

2
  • 3
    System.out.println(sb1.toString().intern() == s1); Commented Mar 11, 2020 at 15:52
  • It works! Thanks Commented Mar 11, 2020 at 18:30

4 Answers 4

2

1. String Interning

As an result of Strings immutability the JVM can make optimization to store only one copy of each literal String in the Java String Pool (the special memory region where Strings are stored by the JVM). This process is called interning

If found, the Java compiler will simply return a reference to its memory address, without allocating additional memory. If not found, it'll be added to the pool and its reference will be returned.

String s1 = "Java";
String s2 = "Java";

System.out.println(s1 == s2); //true

2. Strings Allocated Using the Constructor

Creating a string using the new operator will create a new object and store it in the heap space

Every String created like this will point to a different memory region with its own address

String s1 = "Java";
String s2 = new String("Java"); //or
StringBuilder sb1 = new StringBuilder();

sb1.append("Java");

System.out.println(sb1.toString() == s1);; //false
System.out.println(s2 == s1);; //false

3. Conclusion

When we create a String object using the new() operator, it always creates a new object in heap memory. On the other hand, if we create an object using String literal, it may return an existing object from the String pool

4. Manual Interning

Manually interning the String will store its reference in the pool, and the JVM will return this reference when needed

String s1 = "Java";
String s2 = new String("Java").intern(); //or
StringBuilder sb1 = new StringBuilder();

sb1.append("Java");

System.out.println(sb1.toString().intern() == s1);; //true
System.out.println(s2 == s1);; //true
Sign up to request clarification or add additional context in comments.

1 Comment

I didnt know about interning. This was very helpful and detailed. Thanks so much
2

Because the StringBuilder uses it's own data structure. It does not store the reference to the string, it copies it. There is no way to make it print true.

1 Comment

Thanks Dorian for the answer
2

The implementation (at least for Java8) for StringBuilder.toString() is below:

@Override
public String toString() {
    // Create a copy, don't share the array
    return new String(value, 0, count);
}

Here you can see new String(char value[], int offset, int count) is called so == will be false.

You can test the equivalent code manually below:

String s1 = "Java";
String s2 = "Java";
System.out.println(s1 == s2); //true
System.out.println(new String(s1) == s2); //false
System.out.println(new String(s1.toCharArray(), 0, 4) == s2); //false

Here you can see new String() creates a new Object so == is false.

This can also be seen in the documentation for new String:

Initializes a newly created {@code String} object so that it represents the same sequence of characters as the argument; in other words, the newly created string is a copy of the argument string. Unless an explicit copy of {@code original} is needed, use of this constructor is unnecessary since Strings are immutable.

Note: Java9+ uses byte [] instead of char [] behind the scenes for String, so some of the specifics may have changed, but the idea stays the same.

1 Comment

Thanks Nexevis for the detailed explanation with examples.
1

I have added a couple of more lines to your code to make it clear for you. Feel free to comment in case of any doubt.

public class Main {
    public static void main(String[] args) {
        String s1 = "Java";
        String s2 = "Java";
        StringBuilder sb1 = new StringBuilder();
        sb1.append("Java");
        System.out.println(s1 == s2);
        System.out.println(s1.equals(s2));
        System.out.println(sb1.toString() == s1);
        System.out.println(sb1.toString().equals(s1));

        String s3 = new String("Java"); // It's a new String similar to what sb1.toString() returns
        System.out.println(s1 == s3);// false
    }
}

Output:

true
true
false
true
false

Do check this to explore how StringBuilder::toString has been implemented.

1 Comment

Thank you. That's very helpful.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.