String, StringBuilder, and StringBuffer in Java

Hello!🖖🏼

This article will be about the String class that we frequently use in Java and its alternatives, StringBuilder and StringBuffer. There may be points that I missed, skipped, or inadvertently conveyed incorrectly. If you give me feedback via email in such a situation, I would be very happy.

Now, let’s get started🚀

1.String

First, let’s look at the String class. As we all know, the String class is one of the complex data types in Java that represents character arrays. This class stores an array of characters and provides multiple functions for manipulating these characters. When we want to use it, we define it as shown below and assign a value.

   String str= "Merhaba";

So far, there is no problem here. However, as we know, in Java, the String class is “immutable” meaning it cannot be changed. But of course, some of you might say, “Oh no, I have changed and assigned many times and performed the operations I wanted!

This is where situations arise that prompted me to write this article.

In fact, when we manipulate a String variable, the value that the variable points to does not change 🙂. In such an operation, a new space is opened in the Heap, and the manipulated value is placed there, and our reference points to this new address.

    String str = "Merhaba";
    str.concat("Dünya");

When we define as shown above and then perform the “concat” operation, as mentioned, a new space is created in memory, and our “str” variable now references the address where the “MerhabaDünya” variable is located.

So, a new space is being constantly opened in memory every time the variable is manipulated. And of course, this is not something we would want because it causes both performance and time loss. 🙂

a

  At this point, the StringBuilder class comes to our help.

2.StringBuilder

The role of StringBuilder is also the same as String. It represents character strings. However, the difference from String is that it is a “mutable” or changeable class. Therefore, it is a better choice than the String class for efficient text manipulation.

For example, if we want to convert all the words in a text file to uppercase, the StringBuilder class can easily do this without recreating the entire text in memory (String was creating a new space in memory for this new value).

Or a very simple example like the one below.

    StringBuilder sentence = new StringBuilder("This is a sentence.");
    sentence.append("Added word.");
    System.out.println(sentence.toString()); //This is a sentence.Added word.

However, there is an important drawback to this class that has not yet been mentioned, which is its lack of a thread-safe structure. If a process involving threads is to be used, the StringBuilder class is not suitable in this scenario. This limitation can be explained with the following code snippet:

    StringBuilder sb = new StringBuilder();

    Thread t1 = new Thread(() -> {
        for(int i=0; i<10000; i++){
            sb.append("A");
        }
    });

    Thread t2 = new Thread(() -> {
        for(int i=0; i<10000; i++){
            sb.append("B");
        }
    });

    t1.start();
    t2.start();

    t1.join();
    t2.join();

    System.out.println(sb.toString());

In this example, two distinct threads, t1 and t2, access the StringBuilder object concurrently and append the characters “A” and “B” 10,000 times each in a sequential manner. However, due to the lack of synchronization in the StringBuilder object, the resulting output is indeterminate and can yield varying results on each execution. In essence, a data race condition is introduced.

To address this issue, we can use the StringBuffer class instead.

3.StringBuffer

I won’t repeat the same information with StringBuffer here, if you have read this far, you can already guess that the only difference of this class is its “thread-safe” or synchronized structure. It has the same methods and logic as StringBuilder.

However, this great feature also brings some drawbacks. Its synchronized structure causes it to work slower than StringBuilder.

If we try to explain this with a small code snippet,

    public class StringBufferVsStringBuilder {
        public static void main(String[] args) {
            int N = 1000000;
            long startTime, endTime, duration;

            // StringBuffer performance test
            StringBuffer buffer = new StringBuffer();
            startTime = System.nanoTime();
            for (int i = 0; i < N; i++) buffer.append("hello");
            
            endTime = System.nanoTime();
            duration = (endTime - startTime);
            System.out.println("Time taken by StringBuffer: " + duration + " nanoseconds");
            //Time taken by StringBuffer: 19494000 nanoseconds

            // StringBuilder performance test
            StringBuilder builder = new StringBuilder();
            startTime = System.nanoTime();
            for (int i = 0; i < N; i++) builder.append("hello");
            
            endTime = System.nanoTime();
            duration = (endTime - startTime);
            System.out.println("Time taken by StringBuilder: " + duration + " nanoseconds");
            //Time taken by StringBuilder: 17145000 nanoseconds
        }
    }

This code calculates the elapsed time for StringBuilder and StringBuffer to append the word “hello” one million times. When we run this code, we can easily observe that StringBuffer works slower due to its synchronized structure. We can make this difference more prominent by increasing the value of N.

As a summary, I am adding the features of both classes in the table below,

Features StringBuilder StringBuffer
Thread-Safe No Yes
Performance Faster Slower
Memory Usage Less More
Mutable Yes Yes
 

Finally,

  • There is no harm in using the String class, but we should keep in mind that it is immutable, and every time we try to modify it, a new memory space is created.
  • StringBuilder, on the other hand, performs all operations without the need for creating new memory spaces, but it may provide incorrect results in thread-containing applications since it does not have a synchronized structure.
  • We can solve the problem of StringBuilder by using StringBuffer, which operates in a synchronized structure, but this class may cause some performance loss compared to StringBuilder.

If you have taken the time to read this far, thank you very much. If you notice any mistakes, please feel free to contact me via email.

Happy coding! 🎉    

       

References