What memory (or performance) issues does the two approaches have?

Viewed 137

Being a novice Java developer and having read about the String class and still somewhat confused about it's functionality. So, I decided to write a method to capitalize a user's name in the form, john changes to John using the code below:

1st Approach

   String firstUpper = username.substring(0, 1).toUpperCase();
   String restLower = username.substring(1).toLowerCase();
   return firstUpper.concat(restLower);
   

2nd Approach (method chaining)

   return username.substring(0,1).toUpperCase().concat(username.substring(1).toLowerCase());
3 Answers

No real implications in the performance or any memory inflation.

In the first approach you are declaring two variables that point to the username string content and then make some changes to the string, creating two new strings after that you return the concatenation of those two new strings which result in a third new String.

The second approach you are doing the same thing but with invisible variables (after you substring you always create a new String in the heap). The same thing about the concat operation. So in terms of the virtual machine and in the code produced they will to the same thing and have almost the same code generated (Where strings are create to new spaces in the memory).

You should actually prefer using a StringBuffer or a StringBuilder for such things.

The String Pool has nothing to do with both of your approaches however.

A typical question regarding string pools, will look something like this:

String someString = new String("Shark");

How many strings will be created by the following code snippet? How many strings will exist in the string pool?

And the answer is - only one string will be created, but two strings will exist. The "Shark" String literal will be placed in the String Pool at compilation time, and the constructor invocation will create a new String object at runtime, which will not be in the string pool, but on the heap; and eligible for garbage collection when normal conditions are met.

Had we called .intern() on it - new String("Shark").intern() then the answer chanes to "only one string will be created, and only one string will exist. The .intern() would return the reference to the "Shark" from the string pool, placed there at compilation time, and the string created by new String("Shark") will be garbage collected since nothing is referencing it anymore.

Another example of a string pool question would be this:

        String s1 = "Shark";
        String s2 = "Shark";
        String s3 = new String("Shark");
        String s4 = new String("Shark").intern();

        System.out.println("s1 == s2 :"+(s1==s2));
        System.out.println("s1 == s3 :"+(s1==s3));
        System.out.println("s1 == s4 :"+(s1==s4));

What will be printed out by this code snippet?

Which would of course, print out this:

s1 == s2 :true
s1 == s3 :false
s1 == s4 :true

For completeness, the above example will create exactly one string in the string pool, and one on the heap. One for the two identical "Shark" literals - during compilation time, one on the heap for the new String("Shark") instantiation, and the new String("Shark").intern() will return a reference to the "Shark" literal created at compilation time.

EVERY string literal will end up in the string pool. But your question, as posted, doesn't really have anything to do with the typical string pool questions.

They are mostly equivalent approaches. But if you really want to make this performant and only the first letter needs to be uppercased, then

username.setCharAt(0, (Character.toUpperCase(username.charAt(0)));

You can apply this for the rest of the string in order to make other characters lowercase, like

for (int i = 1; i < username.length(); i++)
    username.setCharAt(i, (Character.toLowerCase(username.charAt(i)));

It might make sense to stress-test this approach against the others (by calling it thousands of times to see how it performs).

EDIT

String pool saves a lot of space at the cost of creating a string in a slightly slower manner (to handle the pool).

String pool helps in saving a lot of space for Java Runtime although it takes more time to create the String.

When we use double quotes to create a String, it first looks for String with the same value in the String pool, if found it just returns the reference else it creates a new String in the pool and then returns the reference.

However using new operator, we force String class to create a new String object in heap space. We can use intern() method to put it into the pool or refer to another String object from the string pool having the same value.

Source: https://www.journaldev.com/797/what-is-java-string-pool

So, if you want to compare String pool against not using it, then you need to create and recreate the same string literals and do it using the new operator as well. In a stress test you can compare their relative performance. If I wanted to do a comparison, I would conduct this test as a start:

for (int i = 0; i < 100000; i++) {
    String foo = "abc";
}

vs.

for (int i = 0; i < 100000; i++) {
    String foo = new String("abc");
}
Related