What is the difference between String and StringBuffer in Java?
You’ve probably thought of this after hearing about String and StringBuffer classes in Java. Looking at some basic examples, both seem to do the same thing – allow you to deal with strings in your program:
public class Main
{
public static void main(String[] args) {
String a1 = new String("Hello World");
StringBuffer a2 = new StringBuffer("Hello World");
System.out.println("This is a string of the String class: "+a1);
System.out.println("This is a string of the StringBuffer class: "+a2);
}
}
Code language: Java (java)
Both classes have functions that perform the same kinds of operations on strings. For example, finding the length of two strings:
public class Main
{
public static void main(String[] args) {
String a1 = new String("Hello World");
StringBuffer a2 = new StringBuffer("Hello World");
System.out.println("Length of string using String class: "+a1.length());
System.out.println("Length of string using StringBuffer class: "+a2.length());
}
}
Code language: Java (java)
So then, what’s the difference between these two? To understand this, we should first understand an important concept in programming.
Immutable VS. Mutable
Objects, or data, in programming languages, can be of two types: Immutable, or Mutable.
Immutable objects cannot be edited, changed, or modified in any way after their creation. Mutable objects can be changed or modified after their creation.
The String class in Java handles strings by making immutable objects. In other words, objects of the String class are immutable. Thus, once defined, they cannot be changed. However, the StringBuffer class in Java creates mutable string objects. These can be edited and modified after their creation. This is the critical difference between these two classes.
A nice analogy to explain this difference is to think about writing words on a page, versus typing words on a text editor. On a text editor, If you type “hrllo” by mistake you can easily go to the second character and change it to an “e”, making your word “hello”. However, if the same mistake happens on a page, you cannot change the “r” to an “e” naturally. You’ll have to scribble or find other ways around it. Writing on a page is innately immutable, whereas typing on a text editor gives mutable strings. It may help to keep this analogy in mind going forward.
StringBuffer – Mutable strings
Let’s discuss the properties of mutable strings practically, and compare these to that of immutable strings.
The two critical properties of mutable strings are:
- Insertion is allowed: We can insert another string or character into the original string at any position.
- Deletion is allowed: We can delete any character or substring from the original string.
All other properties follow from these two properties. Immutable strings do not have these properties. Let’s see an example:
public class Main
{
public static void main(String[] args) {
StringBuffer a = new StringBuffer("Hyllo ");
a.delete(1,2);
// a.delete(startindex,endindex) deletes the substring from startindex(inclusive) to endindex(exclusive)
System.out.println(a);
a.insert(1,"e");
// a.insert(i,str) inserts string str at index i
System.out.println(a);
}
}
Code language: Java (java)
Functions to insert and delete aren’t a part of the String class, and any attempt to perform these actions on objects of the String class will lead to errors. However, as we saw the StringBuffer class provides these functions and lets us edit our string objects.
Other actions like replacing substrings with other substrings can also be done using these two functions itself. In fact, in the example above, we have effectively replaced “y” with “e”. However, to make our life easier, the StringBuffer class has inbuilt functions for these actions too:
public class Main
{
public static void main(String[] args) {
StringBuffer a = new StringBuffer("Highlo ");
a.replace(1,4,"el");
// a.replace(startindex,endindex,str) replaces substring from
//startindex(inclusive) to endindex(exclusive) with str
System.out.println(a);
}
}
Code language: Java (java)
Concatenate VS. Append
Let’s make an explicit distinction between these two so that we can understand the difference between String and StringBuffer better.
Concatenation is joining two or more strings to make a new string.
Appending is adding a string(/sequence of strings) to the end of an existing string.
Based on how we have defined these two terms, concatenation entails the creation of a new object as we join two existing strings to make a new (third) string. However, appending doesn’t entail the creation of a new object, as we simply add a string to the end of an existing string object, thus changing the object.
Since the String class has immutable objects, it cannot support the append operation. However, it supports the concatenate operation. The StringBuffer class supports the append operation and not the concatenate operation.
Append | Concatenate | |
String | ❌ | ✔ |
StringBuffer | ✔ | ❌ |
Concatenate in String
Below is a simple example of how concatenation works in the String class:
public class Main
{
public static void main(String[] args) {
String a = "Hello ";
String b = "World";
String c = a+b; // + operator performs concatenation for the String class
System.out.println(c);
}
}
Code language: Java (java)
It is important to note that we have 3 String objects here, pointed to by variables a,b & c. In other words, c variable holds a new String object.
Append in StringBuffer
Consider the example below:
public class Main
{
public static void main(String[] args) {
StringBuffer a = new StringBuffer("Hello ");
StringBuffer b = new StringBuffer("World");
a.append(b);
System.out.println(a);
}
}
Code language: Java (java)
We do not create any new StringBuffer object in this example. Instead, our original object pointed to by variable a is modified by the append function.
Simulating Concatenate in StringBuffer
Although there is no way to directly concatenate two strings to make a new string using only the StringBuffer class, we can simulate this in two steps:
- Create a new object (string3) using string1.
- Append string2 to string3.
public class Main
{
public static void main(String[] args) {
StringBuffer string1 = new StringBuffer("Hello ");
StringBuffer string2 = new StringBuffer("World");
StringBuffer string3 = new StringBuffer(string1);
string3.append(string2);
System.out.println("string1 is unchanged: "+string1);
System.out.println("string3 is concatenation of string1 and string2: "+string3);
}
}
Code language: Java (java)
Since both our original strings are unchanged, and we have joined them to create a new (third) string object, we have effectively performed concatenation.
Simulating Append in String
There is no way to perform the append operation directly using the String class. However, we can use the concatenate operation as an intermediate step and simulate the append operation:
- Concatenate objects pointed to by variables string1 and string2. This will create a new object.
- Assign this object back to the variable string1.
This effectively looks like we have appended string2 to string1. However, it is important to remember that this involved a concatenation operation, and the new String object created was just assigned back to the original variable string1 to give the illusion of the append operation.
public class Main
{
public static void main(String[] args) {
String string1 = "Hello ";
String string2 = "World";
string1 = string1 + string2;
//The RHS creates a new object by concatenating string1 and string2
//This object is assigned to the LHS, which is the variable string1 itself
System.out.println("string1 appears modified: "+string1);
}
}
Code language: Java (java)
Time Complexity of Append and Concatenate
Let’s say we have two 2 strings, string1 (length m) and string2(length n). Then:
Appending string1 to string2 takes O(n) time, because we simply have to add n characters to the end of string1.
Concatenating string1 and string2 takes O(m+n) time because we have to create a new string object by copying m characters from string1 and then n characters from string2.
Simulated appending in the String class takes O(m+n) times since this simulation involves the concatenation operation.
Simulated concatenation in the StringBuffer class takes O(m+n) time too. This is because we first copy m characters from string1 into a new string which takes O(m) time, and then we append string2 to this new copy which takes O(n) time.
Efficiency issues in String
Sometimes we might need to repeatedly append strings, usually using loops. Such an operation, if done using the String class (by simulating the append operation), can increase the time complexity of the program and make it slower.
Let’s say we want to append string2 of length n to string1 of length m. We want to do this a total of k times. If we are using the String class, we will have to simulate the append operation each time. Thus:
For the first append, it would take O(m+n) time.
For the next append, it would take O((m+n) +n) time, because now the original length of string1 would be (m+n), and we would be simulating an append of string2 (length n) to it.
For the third append, it would take O((m+2*n) +n) time.
and so on.
Thus, if we do this k times, our total time complexity would be:
O(m+n) + O(m+2n) + O(m+3n) +... + O(m+kn)
= O(m + n*(1+2+3... k) )
= O( m+n*k*k)
Code language: Java (java)
In most situations, k would be much greater than the initial length n and m. Thus we can approximate the time complexity as O(n*k*k).
However, if we use the StringBuffer class to do the same program, we can perform an actual append operation. Thus:
For the first append, it would take O(n) time.
Then for the second append, it would take O(n) time.
In fact, for each append, it would just take O(n) time.
Therefore, in total if we do k appends, it would take O(k*n) time.
We see that using the String class increases the time complexity of our program by k times. This is due to the basic difference between String and StringBuffer, that String has immutable objects whereas StringBuffer has mutable objects. This allows StringBuffer to make real append operations that are faster than simulating append operations by copying the entire string1 repeatedly.
Conclusion
In this article, we’ve learned about the difference between the StringBuffer and String class in Java. We’ve seen the extra functions that the StringBuffer class allows us to perform on its mutable objects. We’ve also seen the practical advantage of the StringBuffer class in terms of the efficiency of a program.
Sharing is caring
Did you like what Astitva Sehgal wrote? Thank them for their work by sharing it on social media.
No comments so far
Curious about this topic? Continue your journey with these coding courses: