2

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());
Marome
  • 47
  • 1
  • 11
  • 3
    There is no real difference between these two code snippets. Neither of them demonstrates anything to do with the string pool. – RealSkeptic Mar 11 '20 at 16:55
  • the first approach creates up to 5 strings, the second approach creates one more than the first one, up to 6. I may have misunderstood the question as being more about the "string pool" rather than optimizing this trivial example however. If that's the case, do let me know so i can delete the answer instead of being downvoted to oblivion :D – Shark Mar 11 '20 at 17:08
  • 1
    Literal values are strings of the form `"..."`. There are no literals in your code. – RealSkeptic Mar 12 '20 at 00:28

3 Answers3

2

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).

2

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.

Shark
  • 6,513
  • 3
  • 28
  • 50
  • Your first code snippet (`String someString = new String("Shark");`) will always create one new string. So that part of your answer is wrong. Also, the string pool will not contain the newly created string in your last example. – RealSkeptic Mar 11 '20 at 17:35
  • @RealSkeptic it's not. if the string pool already contained the `"Shark"` literal, the constructor call will make one new string. If it did not contain the "Shark" literal, the literal will create one string in the pool, and the constructor call will create a second one. So `one or two, depending whether the pool already contained the string "Shark"` is correct. – Shark Mar 12 '20 at 10:50
  • All literals in the compilation unit are placed in the string pool in advance. – RealSkeptic Mar 12 '20 at 10:51
  • mhm, so the answer is "one during runtime, because the literal will have been created there during compilation time, thus totalling two" – Shark Mar 12 '20 at 10:53
  • 1
    Since the question was how many were created after that call, the answer is one, and only one. How many string objects *exist*? Two, one in the string pool, one not in the string pool. – RealSkeptic Mar 12 '20 at 10:55
  • did not know that newly created strings don't end up in the string pool unless `.intern()` was explicitly called on it. so, `new String("Shark").intern();` would actually end up with two `Shark` strings in the string pool, or just one? – Shark Mar 12 '20 at 10:59
  • 1
    No. If a string equal to the string being interned has already existed in the string pool, then `intern` will return the reference to the existing copy and do nothing with the string you passed it. That's the whole point of the string pool. If there are no further references to your new string object, it will be eligible for garbage collection. I hope now you understand that your answer was wrong on two counts. – RealSkeptic Mar 12 '20 at 11:06
  • @RealSkeptic yes, indeed - and by misphrasing a few fine details, i've made it's wrongness invisible to the inexperienced eye (as well as overlooking them myself). I'll edit the answer to ammend these issues. – Shark Mar 12 '20 at 11:14
1

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");
}
Lajos Arpad
  • 64,414
  • 37
  • 100
  • 175
  • 1
    nice answer to his example, but it completely skips the point of the "string pool" which i may have misunderstood as the main point of the question.... – Shark Mar 11 '20 at 17:06
  • @Shark thanks for the constructive criticism, I'm editing my answer to remedy this problem. – Lajos Arpad Mar 11 '20 at 17:07
  • no problemo, you're welcome, but I could have been wrong, and misunderstood that this is actually about "string pool" instead of a "how do i optimize this code snippet" :) you may have gotten it right. – Shark Mar 11 '20 at 17:11
  • might also wanna demonstrate using the StringBuilder / StringBuffer for that as well, for optimal performance or optimal performance + thread-safety while at it. – Shark Mar 11 '20 at 17:12
  • @Shark by the way, I like your answer as well. Maybe tomorrow I will add those demonstration into this answer, if time allows it. – Lajos Arpad Mar 11 '20 at 17:17
  • Why are you assuming that `username` is a `StringBuilder`? – RealSkeptic Mar 11 '20 at 17:33
  • If the question is directed to me, I do not assume that. But I might provide an example with a StringBuilder tomorrow. – Lajos Arpad Mar 11 '20 at 17:41
  • 2
    If you did not assume that, then why did you use the `setCharAt` method? You can't do that with a string - it's immutable. – RealSkeptic Mar 11 '20 at 17:54
  • @LajosArpad, the `StringBuilder` approach seems more efficient as most prefer it over `String` when working with mutable objects; `return new StringBuilder().append(username.substring(0, 1) .toUpperCase()).append(username.substring(1).toLowerCase()).toString();` – Marome Jun 29 '20 at 14:51
  • An article I found useful -> https://dzone.com/articles/string-concatenation-performacne-improvement-in-ja – Marome Jun 29 '20 at 14:52
  • @Marome thanks for the reflection on our conversation. Your comments are valuable. – Lajos Arpad Jun 30 '20 at 08:45
  • @LajosArpad, your comments have been of great assistance as well, thank you. – Marome Jun 30 '20 at 12:35
  • @Marome I'm glad to hear that, thanks for letting me know. – Lajos Arpad Jul 01 '20 at 09:57