A Brief Summary of the String Class
A Java String contains an immutable sequence of Unicode characters. Unlike C/C++, where string is simply an array of char, A Java String is an object of the class java.lang.
Java String is, however, special. Unlike an ordinary class:
String is associated with string literal in the form of double-quoted texts such as "Hello, world!". You can assign a string literal directly into a String variable, instead of calling the constructor to create a String instance.
The '+' operator is overloaded to concatenate two String operands. '+' does not work on any other objects such as Point and Circle.
String is immutable. That is, its content cannot be modified once it is created. For example, the method toUpperCase() constructs and returns a new String instead of modifying the its existing content.
Static method String.format() (JDK 1.5)
The static method String.format() (introduced in JDK 1.5) can be used to produce a formatted String using C-like printf()'s format specifiers. The format() method has the same form as printf(). For example,
String.format("%.1f", 1.234); // returns String "1.2"
String.format() is useful if you need to produce a simple formatted String for some purposes (e.g., used in method toString()). For complex string, use StringBuffer/StringBuilder with a Formatter. If you simply need to send a simple formatted string to the console, use System.out.printf(), e.g.,
System.out.printf("%.1f", 1.234);
1. String Literal vs. String Object
There are two ways to construct a string: implicit construction by assigning a string literal or explicitly creating a
String
object via the new
operator and constructor. For example,String s1 = "Hello"; // String literal String s2 = "Hello"; // String literal String s3 = s1; // same reference String s4 = new String("Hello"); // String object String s5 = new String("Hello"); // String object
Java has provided a special mechanism for keeping the
String
literals - in a so-called string common pool. If two string literals have the same contents, they will share the same storage inside the common pool. This approach is adopted to conserve storage for frequently-used strings. On the other hand,String
objects created via the new
operator and constructor are kept in the heap. Each String
object in the heap has its own storage just like any other object. There is no sharing of storage in heap even if two String
objects have the same contents.
You can use the method
equals()
of the String
class to compare the contents of two String
s. You can use the relational equality operator '=='
to compare the references (or pointers) of two objects. Study the following codes:s1 == s1; // true, same pointer s1 == s2; // true, s1 and s1 share storage in common pool s1 == s3; // true, s3 is assigned same pointer as s1 s1.equals(s3); // true, same contents s1 == s4; // false, different pointers s1.equals(s4); // true, same contents s4 == s5; // false, different pointers in heap s4.equals(s5); // true, same contents
Important Notes:
- In the above example, I used relational equality operator
'=='
to compare the references of twoString
objects. This is done to demonstrate the differences between string literals sharing storage in the common pool andString
objects created in the heap. It is a logical error to use(str1 == str2)
in your program to compare the contents of twoString
s. String
can be created by directly assigning aString
literal which is shared in a common pool. It is uncommon and not recommended to use thenew
operator to construct aString
object in the heap.
2. Difference Between String, StringBuilder and StringBuffer Classes
String
String is immutable ( once created can not be changed )object . The object created as a String is stored in the Constant String Pool .
Every immutable object in Java is thread safe ,that implies String is also thread safe . String can not be used by two threads simultaneously.
String once assigned can not be changed .
String demo = " hello " ;
// The above object is stored in constant string pool and its value can not be modified.
demo="Bye" ; //new "Bye" string is created in constant pool and referenced by the demo variable
// "hello" string still exists in string constant pool and its value is not overrided but we lost reference to the "hello"string
StringBuffer
StringBuffer is mutable means one can change the value of the object . The object created through StringBuffer is stored in the heap . StringBuffer has the same methods as the StringBuilder , but each method in StringBuffer is synchronized that is StringBuffer is thread safe .
Due to this it does not allow two threads to simultaneously access the same method . Each method can be accessed by one thread at a time .
But being thread safe has disadvantages too as the performance of the StringBuffer hits due to thread safe property . Thus StringBuilder is faster than the StringBuffer when calling the same methods of each class.
StringBuffer value can be changed , it means it can be assigned to the new value . Nowadays its a most common interview question ,the differences between the above classes .
String Buffer can be converted to the string by using
toString() method.
StringBuffer demo1 = new StringBuffer("Hello") ;
// The above object stored in heap and its value can be changed .
demo1=new StringBuffer("Bye");
// Above statement is right as it modifies the value which is allowed in the StringBuffer
StringBuilder
StringBuilder is same as the StringBuffer , that is it stores the object in heap and it can also be modified . The main difference between the StringBuffer and StringBuilder is that StringBuilder is also not thread safe.
StringBuilder is fast as it is not thread safe .
StringBuilder demo2= new StringBuilder("Hello");
// The above object too is stored in the heap and its value can be modified
demo2=new StringBuilder("Bye");
// Above statement is right as it modifies the value which is allowed in the StringBuilder
String | StringBuffer | StringBuilder | |
Storage Area | Constant String Pool | Heap | Heap |
Modifiable | No (immutable) | Yes( mutable ) | Yes( mutable ) |
Thread Safe | Yes | No | No |
Performance | Fast | Very Slow | Fast |
The StringBuffer and StringBuilder classes are used when there is a necessity to make a lot of modifications to Strings of characters. The StringBuilder class was introduced as of Java 5 and the main difference between the StringBuffer and StringBuilder is that StringBuilders methods are not thread safe(not Synchronised).
It is recommended to use StringBuilder whenever possible because it is faster than StringBuffer. However if thread safety is necessary the best option is StringBuffer objects.
3. Benchmarking String/StringBuffer/StringBuilder
The following program compare the times taken to reverse a long string via a
String
object and a StringBuffer
.// Reversing a long String via a String vs. a StringBuffer public class StringsBenchMark { public static void main(String[] args) { long beginTime, elapsedTime; // Build a long string String str = ""; int size = 16536; char ch = 'a'; beginTime = System.nanoTime(); // Reference time in nanoseconds for (int count = 0; count < size; ++count) { str += ch; ++ch; if (ch > 'z') { ch = 'a'; } } elapsedTime = System.nanoTime() - beginTime; System.out.println("Elapsed Time is " + elapsedTime/1000 + " sec (Build String)"); // Reverse a String by building another String character-by-character in the reverse order String strReverse = ""; beginTime = System.nanoTime(); for (int pos = str.length() - 1; pos >= 0 ; pos--) { strReverse += str.charAt(pos); // Concatenate } elapsedTime = System.nanoTime() - beginTime; System.out.println("Elapsed Time is " + elapsedTime/1000 + " sec (Using String to reverse)"); // Reverse a String via an empty StringBuffer by appending characters in the reverse order beginTime = System.nanoTime(); StringBuffer sBufferReverse = new StringBuffer(size); for (int pos = str.length() - 1; pos >= 0 ; pos--) { sBufferReverse.append(str.charAt(pos)); // append } elapsedTime = System.nanoTime() - beginTime; System.out.println("Elapsed Time is " + elapsedTime/1000 + " sec (Using StringBuffer to reverse)"); // Reverse a String by creating a StringBuffer with the given String and invoke its reverse() beginTime = System.nanoTime(); StringBuffer sBufferReverseMethod = new StringBuffer(str); sBufferReverseMethod.reverse(); // use reverse() method elapsedTime = System.nanoTime() - beginTime; System.out.println("Elapsed Time is " + elapsedTime/1000 + " sec (Using StringBuffer's reverse() method)"); // Reverse a String via an empty StringBuilder by appending characters in the reverse order beginTime = System.nanoTime(); StringBuilder sBuilderReverse = new StringBuilder(size); for (int pos = str.length() - 1; pos >= 0 ; pos--) { sBuilderReverse.append(str.charAt(pos)); } elapsedTime = System.nanoTime() - beginTime; System.out.println("Elapsed Time is " + elapsedTime/1000 + " usec (Using StringBuilder to reverse)"); // Reverse a String by creating a StringBuilder with the given String and invoke its reverse() beginTime = System.nanoTime(); StringBuffer sBuilderReverseMethod = new StringBuffer(str); sBuilderReverseMethod.reverse(); elapsedTime = System.nanoTime() - beginTime; System.out.println("Elapsed Time is " + elapsedTime/1000 + " sec (Using StringBuidler's reverse() method)"); } } |
Elapsed Time is 332100 usec (Build String) Elapsed Time is 346639 usec (Using String to reverse) Elapsed Time is 2028 usec (Using StringBuffer to reverse) Elapsed Time is 847 usec (Using StringBuffer's reverse() method) Elapsed Time is 1092 usec (Using StringBuilder to reverse) Elapsed Time is 836 usec (Using StringBuidler's reverse() method)
Observe
StringBuilder
is 2x faster than StringBuffer
, and 300x faster than String
. The reverse()
method is the fastest, which take about the same time for StringBuilder
and StringBuffer
.