A small program can create a lot of String objects very quickly. For example, how many do you think this piece of code creates?
String alpha = ""; for(char current = 'a'; current <= 'z'; current++) alpha += current; System.out.println(alpha);
The empty String on line 1 is instantiated, and then line 2 appends an "a". However, because the String object is immutable, a new String object is assigned to alpha and the “” object becomes eligible for garbage collection. The next time through the loop, alpha is assigned a new String object, "ab", and the "a" object becomes eligible for garbage collection. The next iteration assigns alpha to "abc" and the "ab" object becomes eligible for garbage collection, and so on.
This sequence of events continues, and after 26 iterations through the loop, a total of 27 objects are instantiated, most of which are immediately eligible for garbage collection.
This is very inefficient. Luckily, Java has a solution. The StringBuilder class creates a String without storing all those interim String values. Unlike the String class, StringBuilder is not immutable.
StringBuilder alpha = new StringBuilder(); for(char current = 'a'; current <= 'z'; current++) alpha.append(current);18: System.out.println(alpha);
On line 1, a new StringBuilder object is instantiated. The call to
append() on line 3 adds a character to the StringBuilder object each time through the for loop and appends the value of current to the end of alpha. This code reuses the same StringBuilder without creating an interim String each time.
In this section, we'll look at creating a StringBuilder, common methods, and a comparison to StringBuffer.
Mutability and Chaining
We're sure you noticed this from the previous example, but StringBuilder is not immutable. In fact, we gave it 27 different values in the example (blank plus adding each letter in the alphabet). The exam will likely try to trick you with respect to String and StringBuilder being mutable.
Chaining makes this even more interesting. When we chained String method calls, the result was a new String with the answer. Chaining StringBuilder objects doesn't work this way. Instead, the StringBuilder changes its own state and returns a reference to itself! Let's look at an example to make this clearer:
StringBuilder sb = new StringBuilder("start"); sb.append("+middle"); // sb = "start+middle" StringBuilder same = sb.append("+end"); // "start+middle+end"
Line 2 adds text to the end of sb. It also returns a reference to sb, which is ignored. Line 6 also adds text to the end of sb and returns a reference to sb. This time the reference is stored in same—which means sb and same point to the exact same object and would print out the same value.
The exam won't always make the code easy to read by only having one method per line. What do you think this example prints?
StringBuilder a = new StringBuilder("abc"); StringBuilder b = a.append("de"); b = b.append("f").append("g"); System.out.println("a=" + a); System.out.println("b=" + b);
Did you say both print "abcdefg"? Good. There's only one StringBuilder object here. We know that because new StringBuilder() was called only once. On line 2, there are two variables referring to that object, which has a value of "abcde". On line 3, those two variables are still referring to that same object, which now has a value of "abcdefg". Incidentally, the assignment back to b does absolutely nothing. b is already pointing to that StringBuilder.
Creating a StringBuilder
There are three ways to construct a StringBuilder:
StringBuilder sb1 = new StringBuilder(); StringBuilder sb2 = new StringBuilder("animal"); StringBuilder sb3 = new StringBuilder(10);
The first says to create a StringBuilder containing an empty sequence of characters and assign sb1 to point to it. The second says to create a StringBuilder containing a specific value and assign sb2 to point to it. For the first two, it tells Java to manage the implementation details. The final example tells Java that we have some idea of how big the eventual value will be and would like the StringBuilder to reserve a certain number of slots for characters.
Size vs. Capacity
The behind-the-scenes process of how objects are stored isn't on the exam, but some knowledge of this process may help you better understand and remember StringBuilder.
Size is the number of characters currently in the sequence, and capacity is the number of characters the sequence can currently hold. Since a String is immutable, the size and capacity are the same. The number of characters appearing in the String is both the size and capacity.
For StringBuilder, Java knows the size is likely to change as the object is used. When StringBuilder is constructed, it may start at the default capacity (which happens to be 16) or one of the programmer's choosing. In the example, we request a capacity of 5. At this point, the size is 0 since no characters have been added yet, but we have space for 5.
Next we add four characters. At this point, the size is 4 since four slots are taken. The capacity is still 5. Then we add three more characters. The size is now 7 since we have used up seven slots. Because the capacity wasn't large enough to store seven characters, Java automatically increased it for us.
Important StringBuilder Methods
As with String, we aren't going to cover every single method in the StringBuilder class.
charAt(), indexOf(), length(), and substring()
These four methods work exactly the same as in the String class. Be sure you can identify the output of this example:
StringBuilder sb = new StringBuilder("animals"); String sub = sb.substring(sb.indexOf("a"), sb.indexOf("al")); int len = sb.length();char ch = sb.charAt(6); System.out.println(sub + " " + len + " " + ch);
The correct answer is anim 7 s. The
indexOf() method calls return 0 and 4, respectively.
substring() returns the String starting with index 0 and ending right before index 4.
length() returns 7 because it is the number of characters in the StringBuilder rather than an index. Finally,
charAt() returns the character at index 6. Here we do start with 0 because we are referring to indexes. If any of this doesn't sound familiar, go back and read the section on String again.
substring() returns a String rather than a StringBuilder. That is why sb is not changed.
substring() is really just a method that inquires about where the substring happens to be.
append() method is by far the most frequently used method in StringBuilder. In fact, it is so frequently used that we just started using it without comment. Luckily, this method does just what it sounds like: it adds the parameter to the StringBuilder and returns a reference to the current StringBuilder. One of the method signatures is as follows:
StringBuilder append(String str)
Notice that we said one of the method signatures. There are more than 10 method signatures that look similar but that take different data types as parameters. All those methods are provided so you can write code like this:
StringBuilder sb = new StringBuilder().append(1).append('c'); sb.append("-").append(true); System.out.println(sb); // 1c-true
Nice method chaining, isn't it?
append() is called directly after the constructor. By having all these method signatures, you can just call
append() without having to convert your parameter to a String first.
insert() method adds characters to the StringBuilder at the requested index and returns a reference to the current StringBuilder. Just like
append(), there are lots of method signatures for different types. Here's one:
StringBuilder insert(int offset, String str)
Pay attention to the offset in these examples. It is the index where we want to insert the requested parameter.
StringBuilder sb = new StringBuilder("animals"); sb.insert(7, "-"); // sb = animals- sb.insert(0, "-"); // sb = -animals- sb.insert(4, "-"); // sb = -ani-mals System.out.println(sb);
Line 2 says to insert a dash at index 7, which happens to be the end of sequence of characters. Line 3 says to insert a dash at index 0, which happens to be the very beginning. Finally, line 4 says to insert a dash right before index 4. The exam creators will try to trip you up on this. As we add and remove characters, their indexes change. When you see a question dealing with such operations, draw what is going on so you won't be confused.
delete() and deleteCharAt()
delete() method is the opposite of the
insert() method. It removes characters from the sequence and returns a reference to the current StringBuilder. The
deleteCharAt() method is convenient when you want to delete only one character. The method signatures are as follows:
StringBuilder delete(int start, int end) StringBuilder deleteCharAt(int index)
The following code shows how to use these methods:
StringBuilder sb = new StringBuilder("abcdef"); sb.delete(1, 3); // sb = adef sb.deleteCharAt(5); // throws an exception
First, we delete the characters starting with index 1 and ending right before index 3. This gives us adef. Next, we ask Java to delete the character at position 5. However, the remaining value is only four characters long, so it throws a
After all that, it's time for a nice, easy method. The reverse() method does just what it sounds like: it reverses the characters in the sequences and returns a reference to the current
StringBuilder. The method signature is as follows:
The following code shows how to use this method:
StringBuilder sb = new StringBuilder("ABC"); sb.reverse(); System.out.println(sb);
As expected, this prints CBA. This method isn't that interesting. Maybe the exam creators like to include it to encourage you to write down the value rather than relying on memory for indexes.
The last method converts a StringBuilder into a String. The method signature is as follows:
The following code shows how to use this method:
String s = sb.toString();
Often StringBuilder is used internally for performance purposes but the end result needs to be a String. For example, maybe it needs to be passed to another method that is expecting a String.
StringBuilder vs. StringBuffer
When writing new code that concatenates a lot of String objects together, you should use StringBuilder. StringBuilder was added to Java in Java 5. If you come across older code, you will see StringBuffer used for this purpose. StringBuffer does the same thing but more slowly because it is thread safe. You'll learn about threads for the OCP exam. In theory, you don't need to know about StringBuffer on the exam at all. However, we bring this up anyway, since an older question might still be left on the exam.