7.5. From the Java Library: java.lang.StringBufferOne problem with the keywordSearch() method is that it is not very efficient because a String in Java is a read-only object. This means that a String cannot be changed once it has been instantiated. You cannot insert new characters or delete existing characters.
Java Language Rule: Strings Are Immutable
Given this fact, how is it possible that the resultStr in the keywordSearch() ends up with the correct value? The answer is that every time we assign a new value to resultStr, Java has to create a new String object. Figure 7.9 illustrates the process. Thus, given the statement Figure 7.9. Evaluating resultStr = resultStr + ptr + " " creates an orphan object that must be garbage collected. |
An object that has no reference to it can no longer be used in a program. Therefore, Java will automatically get rid of it. This is known as garbage collection. |
A more efficient way to write the keywordSearch() method would make use of a StringBuffer to store and construct the resultStr. Like the String class, the java.lang.StringBuffer class also represents a string of characters. However, unlike the String class, a StringBuffer can be modified, and it can grow and shrink in length as necessary. As Figure 7.10 shows, the StringBuffer class contains several of the same kinds of methods as the String classfor example, charAt() and length(). But it also contains methods that allow characters and other types of data to be inserted into a string, such as append(), insert(), and setCharAt(). Most string-processing algorithms use StringBuffers instead of Strings as their preferred data structure.
Choosing the appropriate data structure
Java Programming Tip: StringBuffer
A StringBuffer should be used instead of a String for any task that involves modifying a string. |
The StringBuffer class provides several methods that are useful for string processing. The constructor method, StringBuffer(String), makes it easy to convert a String into a StringBuffer. Similarly, once you are done processing the buffer, the toString() method makes it easy to convert a StringBuffer back into a String.
The typical way to use a StringBuffer is shown in the following revised version of the keywordSearch() method:
public String keywordSearch(String s, String keyword) { // Create StringBuffer StringBuffer resultStr = new StringBuffer(); int count = 0; int ptr = s.indexOf(keyword); while (ptr != -1) { ++count; resultStr.append(ptr + " "); // Append to buffer ptr = s.indexOf(keyword, ptr + 1); } resultStr.insert(0, count + ": "); return resultStr.toString(); // Convert buffer to String } // keywordSearch()
We declare resultStr as a StringBuffer instead of a String. Then, instead of concatenating the ptr and reassigning the resultStr, we append() the ptr to the resultStr for each occurrence of a keyword. Similarly, after the loop exits, we insert() the count at the front (index 0) of the resultStr. Finally, we convert resultStr into a String by using the toString() method before returning the method's result.
One advantage of the StringBuffer class is that there are several versions of its insert() and append() methods. These make it possible to insert any type of dataint, double, Object, and so oninto a StringBuffer. The method itself takes care of converting the data into a string for us.
To summarize, String objects in Java are immutable. So when a String is "modified," this really means that a new String object is created and the old String object must be garbage collected. This is somewhat inefficient, especially if done repeatedly within a loop. To avoid these inefficiencies, use a StringBuffer instead of a String in such contexts.
Strings are immutable