The java.lang.StringBuffer and java.lang.StringBuilder classes should be used when you have to make a lot of modifications to strings of characters. As we discussed in the previous chapter, String objects are immutable, so if you choose to do a lot of manipulations with String objects, you will end up with a lot of abandoned String objects in the String pool. On the other hand, objects of type StringBuffer and StringBuilder can be modified over and over again without leaving behind a great list of discarded String objects.
StringBuffer vs. StringBuilder
The StringBuilder class was added in Java 5. It has exactly the same API as the StringBuffer class, except StringBuilder is not thread safe. In other words, its methods are not synchronized. (For now just know that syncrhonized is used for thread safety and causes an overhead in terms of performance) Sun recommends that you use StringBuilder instead of StringBuffer whenever possible because StringBuilder will run faster. So apart from synchronization, anything we say about StringBuilder’s methods holds true for StringBuffer’s methods, and vice versa.
Don't worry about Threading and Synchronization now. We will have an in-depth coverage of them, probably to the point where you’ll get bored in one of the subsequent chapters. So for now, just sit tight and enjoy the StringBuider/Buffer classes
Using StringBuilder and StringBuffer
In the previous section, we saw how the exam might test your understanding of String immutability with code fragments like this:
String x = "abc";
System.out.println("x = " + x);
// output is "x = abc"
Because no new assignment was made, the new String object created with the concat() method was abandoned instantly. We also saw examples like this:
String x = "abc";
x = x.concat("def");
System.out.println("x = " + x);
// output is "x = abcdef"
We got a nice new String out of the deal, but the downside is that the old String “abc” has been lost in the String pool, thus wasting memory. If we were using a StringBuffer instead of a String, the code would look like this:
StringBuffer sb = new StringBuffer("abc");
System.out.println("sb = " + sb);
// output is "sb = abcdef"
All of the StringBuffer methods we will discuss operate on the value of the StringBuffer object invoking the method. So a call to sb.append(“def”); is actually appending “def” to itself (StringBuffer sb). In fact, these method calls can be chained to each other—for example,
StringBuilder sb = new StringBuilder("abc");
System.out.println( sb );
// output is "fed---cba"
Notice that in each of the previous two examples, there was a single call to new, concordantly in each example we weren’t creating any extra objects. Each example needed only a single StringXxx object to execute.
Important Methods in the StringBuffer and StringBuilder Classes
The following are the important methods that you can use with either the StringBuffer or StringBuilder class.
Note: I am using StringBuffer in some cases and StringBuilder in other cases. The fact is, either class has these methods and so the usage of either of the class names is interchangeable.
public synchronized StringBuffer append(String s)
As we’ve seen earlier, this method will update the value of the object that invoked the method, whether or not the return is assigned to a variable. This method will take many different arguments, including boolean, char, double, float, int, long, and others, but the most likely use on the exam will be a String argument—for example,
StringBuffer sb = new StringBuffer("ferrari ");
System.out.println(sb); // output is "ferrari car"
public StringBuilder delete(int start, int end)
This method returns a StringBuilder object and updates the value of the StringBuilder object that invoked the method call. In both cases, a substring is removed from the original object. The starting index of the substring to be removed is defined by the first argument (which is zero-based), and the ending index of the substring to be removed is defined by the second argument (but it is one-based)! Study the following example carefully:
StringBuilder sb = new StringBuilder("0123456789");
// output is "01236789"
public StringBuilder insert(int offset, String s)
This method returns a StringBuilder object and updates the value of the StringBuilder object that invoked the method call. In both cases, the String passed in to the second argument is inserted into the original StringBuilder starting at the offset location represented by the first argument (the offset is zero-based). Again, other types of data can be passed in through the second argument (boolean, char, double, float, int, long, and so on), but the String argument is the one you’re most likely to see:
StringBuilder sb = new StringBuilder("ferrari");
System.out.println( sb ); // output is "ferr---ari"
public synchronized StringBuffer reverse()
This method returns a StringBuffer object and updates the value of the StringBuffer object that invoked the method call. In both cases, the characters in the StringBuffer are reversed, the first character becoming the last, the second becoming the second to the last, and so on:
StringBuffer s = new StringBuffer("Rocky");
System.out.println(sb); // output: "ykcoR"
public String toString()
This method returns the value of the StringBuffer object that invoked the method call as a String:
StringBuffer sb = new StringBuffer("test string");
System.out.println( sb.toString() ); // output is "test string"
So, if you see, the StringBuffer and StringBuilder classes are almost similar to the String class. The only difference being the fact that, unlike Strings, they can be changed.
Exam Tip: Many of the exam questions covering this chapter’s topics use a tricky (and not very readable) bit of Java syntax known as “chained methods.” A statement with chained methods has this general form:
result = method1().method2().method3();
In theory, any number of methods can be chained in this fashion, although typically you won’t see more than three. Here’s how to decipher these “handy Java shortcuts” when you encounter them:
1. Determine what the leftmost method call will return (let’s call it x).
2. Use x as the object invoking the second (from the left) method. If there are only two chained methods, the result of the second method call is the expression’s result.
3. If there is a third method, the result of the second method call is used to invoke the third method, whose result is the expression’s result—for example,
4. String x = "abc";
5. String y = x.concat("def").toUpperCase().replace('C','x');
6. //chained methods
System.out.println("y = " + y); // result is "y = ABxDEF"
Let’s look at what happened. The literal def was concatenated to abc, creating a temporary, intermediate String (soon to be lost), with the value abcdef. The toUpperCase() method created a new (soon to be lost) temporary String with the value ABCDEF. The replace() method created a final String with the value ABxDEF, and referred y to it.
Previous Chapter: Chapter 32 - String Class
Next Chapter: Chapter 34 - File Navigation and I/O