Chapter 10

Working with Java Strings


CONTENTS

In this chapter you learn about the String and StringBuffer classes, two of the classes you will use most in your Java programming career. You learn how to convert other object types into strings so they can be displayed. You also learn how to perform a number of other operations on strings, including case conversion, searching for characters or substrings, and comparing strings. You learn how the StringBuffer class offers an alternative to the String class and is more useful when working with text that is likely to change. And finally, you learn how to use the many methods for appending or inserting into a StringBuffer.

The String Class

The Java String class, a part of the java.lang package, is used to represent strings of characters. Unlike C and C++, Java does not use an array of characters to represent a string. The String class is used to represent a string that is fairly static, changing infrequently if at all. This section describes how to use this class and includes examples of many of the nearly 50 methods or constructors that are a part of this important class.

Constructing New Strings

There are seven String constructors, as shown in Table 10.1. As examples of using these constructors, consider the following code:

String str1 = new String();
String str2 = new String("A New String");
char charArray[] = {'A', 'r', 'r', 'a', 'y' };
String str3 = new String(charArray);
String str4 = new String(charArray, 2, 3);
StringBuffer buf = new StringBuffer("buffer");
String str5 = new String(buf);

Table 10.1. Constructors for the String class.

ConstructorPurpose
String()Creates an empty string.
String(String)Creates a string from the specified string.
String(char[])Creates a string from an array of characters.
String(char[], int, int)Creates a string from the specified subset of characters in an array.
String(byte[], int)Creates a string from the specified byte array and Unicode upper byte.
String(byte[],int,int,int)Creates a string from the specified subset of bytes and Unicode upper byte.
String(StringBuffer)Creates a string.

In the first example, str1 is created as an empty string. The second string, str2, will hold the text "A New String". The next two examples, str3 and str4, are constructed from a character array. In the case of str3, the entire array is placed in the string. For str4, three characters starting in array position two are copied into the string. Because Java arrays are zero-based, this results in str4 containing "ray". In the final example, the string str5 is constructed from a StringBuffer, buf. The StringBuffer class has much in common with String and is described in detail later in this chapter.

In addition to these constructors, there are other ways to get a String object. Many Java classes include a toString method that can be used to generate a string representation of the object. For example, consider the following:

Long myLong = new Long(43);
String longStr = myLong.toString();
g.drawString(longStr + myLong.toString(), 10, 20);

In this case, a Long object is created and set to hold the value 43. Note that this is an object of type Long, not a primitive of type long. Because myLong is an instance of a class, the toString method is used to place a string directly into longStr. The call to drawString illustrates that the converted string in longStr can be used as a regular string as can an inline use of myLong.toString.

Finally, Java supports the use of automatic strings, as follows:

g.drawString("This is an automatic string.", 10, 20);

In this case, the drawString method expects to be passed a string as its first parameter. To satisfy this expectation, a new String object is constructed using the quoted text. This happens behind the scenes automatically and requires no special attention on your part.

Basic String Methods

The basic methods for manipulating and examining a string are shown in Table 10.2. The Java String class is meant to hold text that does not change, so this table does not include a lot of methods for adding and inserting text. It does, however, include a simple concat method for adding one string to the end of another and a replace method for swapping one character for another.

Table 10.2. Basic string methods.

MethodPurpose
concat(String)Concatenates one string onto another.
length()Returns the length of the string.
replace(char, char)Replaces all occurrences of one character with a different character.
toLowerCase()Converts the entire string to lowercase.
toUpperCase()Converts the entire string to uppercase.
trim()Trims both leading and trailing whitespace from the string.

The concat method appends the specified string onto the current string and returns the result as a new string. This can be seen in the following, in which str3 will contain "Hello World":

String str1 = new String("Hello ");
String str2 = new String("World");
String str3 = str1.concat(str2);

The replace method can be used to change all occurrences of one character to a different character. The following example changes "Hi Mom" to "Hi Mum", allowing the program to be run in England:

String str = new String("Hi Mom ");
String newStr = str.replace('o', 'u');

Like concat, trim works by returning a new string. In this case, all leading and trailing whitespace characters are first removed from the returned string. The following example will remove the tabs, carriage return, new line, and space characters at both ends of the string:

String str = new String("\t\t In The Middle \r\n");
String newStr = str.trim();

The length method simply returns the number of characters in the string. In the following example, len will be set to 12:

String str = new String("Length of 12");
int len = str.length();

The toUpperCase and toLowerCase methods can be used to change the case of an entire string. Each works by returning a new string. In the following example, an uppercase version and a lowercase version of the same string are created:

String str = new String("This is MiXeD caSE");
String upper = str.toUpperCase();
String lower = str.toLowerCase();

Converting Variables to Strings

Because most of the display methods of Java's user interface classes use strings, it is important to be able to convert variables of other types into strings. Naturally, Java includes methods for doing this. The String class includes a set of static valueOf methods that can be used to create strings from other Java objects and primitive types. The valueOf methods are shown in Table 10.3.

Table 10.3. Useful methods for converting strings.

MethodPurpose
valueOf(char)Returns a string containing the one specified character.
valueOf(char[])Returns a string with the same text as the specified character array.
valueOf(char[], int, int)Returns a string with the same text as a subset of the specified character array.
valueOf(boolean)Returns a string containing either true or false.
valueOf(int)Returns a string containing the value of the int.
valueOf(long)Returns a string containing the value of the long.
valueOf(float)Returns a string containing the value of the float.
valueOf(double)Returns a string containing the value of the double.

Each of these methods is passed the variable to convert and returns a string representation of that variable. Because these methods are static, they are invoked using the name of the class rather than the name of an instance of the class. For example, consider the following uses of valueOf to create strings from numeric values:

String intStr = String.valueOf(100);
String floatStr = String.valueOf(98.6F);
String doubleStr = String.valueOf(3.1416D);

Each of these lines will result in the creation of a string containing the number specified. Consider also the following examples of non-numeric conversions:

String boolStr = String.valueOf(true);
String charStr = String.valueOf('Y');
char charArray[] = {'A', 'r', 'r', 'a', 'y' };
String arrayStr1 = String.valueOf(charArray);
String arrayStr2 = String.valueOf(charArray, 2, 3);

In the first case, boolStr will be set to true. The variable charStr will be a string with a length of 1. The entire five-character array will be stored in arrayStr1, but only ray will be stored in arrayStr2.

Using Only Part of a String

Of course, sometimes the entire string is too much; you just need a portion of the string. Fortunately, the Java String class provides methods for accessing or creating a substring from a longer string. Each of the methods listed in Table 10.4 can be used to retrieve a portion of a string.

Table 10.4. Methods for using a substring.

MethodPurpose
charAt(int)Returns the character at the specified location.
getBytes(int, int, byte[], int) Copies the specified number of characters from the specified location into the byte array.
getChars(int, int, char[], int) Copies the specified number of characters from the specified location into the char array.
substring(int)Returns a substring beginning at the specified offset of the current string.
substring(int, int)Returns a substring between the specified offsets of the current string.

The charAt method retrieves the single character at the index given. For example, the following will place the character a in ch:

String str = new String("This is a String");
char ch = str.charAt(8);

The getBytes and getChars methods are similar. The first moves a substring into an array of bytes; the latter moves a substring into an array of characters. As an example, consider the following:

public void paint(Graphics g)
{
    String str = new String("Wish You Were Here");
    char charArray[] = new char[25];
    str.getChars(5, 13, charArray, 0);
    g.drawChars(charArray, 0, 8, 10, 10);
}

This code will extract You Were from str and move it into the charArray variable. It is then displayed using g.drawChars.

The two substring methods can be used to create new strings that are extracted from the current string. To create a substring from an offset to the end of the string, use the first version of substring, as follows:

String str = new String("This is a String");
String substr = str.substring(10);

In this case substr will hold String. To create a substring from the middle of a string, you can specify an ending offset as an additional parameter to substring. The following code will place You Were into substr:

String str = new String("Wish You Were Here");
String substr = str.substring(5, 13);

The character given by the beginning index will be included in the new string, but the character given by the ending index will not be.

Comparing Strings

As can be seen in Table 10.5, Java provides a number of methods for comparing one string to another. The compareTo method returns the difference between two strings by examining the first two characters that differ in the strings. The difference between the characters is returned. For example, the character a differs from c by two characters, so 2 will be returned if these are the first two characters that differ. Depending on which string contains the higher valued character, the return value could be positive or negative. For example, in the following, result1 will equal -2 but result2 will be 2.

String str1 = new String("abc");
String str2 = new String("abe");

// compare str2 against str1
int result1 = str1.compareTo(str2);

// perform the same comparison but in the
// opposite direction (str1 against str2)
int result2 = str2.compareTo(str1);

Table 10.5. Methods for comparing strings.

MethodPurpose
compareTo(String)Compares two strings. Returns 0 if they are equal, a negative value if the specified string is greater than the string, or a positive value otherwise.
endsWith(String)Returns true if the string ends with the specified string.
equalIgnoreCaseReturns true if, ignoring differences in capitalization, the string matches the specified string.
equals(Object)Returns true if the string matches the object.
equalTo(String)Returns true if the string matches the specified string.
regionMatches(int,
String,int,int)
Returns true if the specified region of the string matches the specified region of a different string.
regionMatches(boolean,
int,String,int,int)
Returns true if the specified region of the string matches the specified region of a different string optionally considering the case of the strings.
startsWith(String)Returns true if the string starts with the specified string.
startsWith(String, int)Returns true if the string starts with the specified string at the specified offset.

The equals and equalTo methods perform similar comparisons; however, these each return a boolean value rather than a measure of how the strings differ. These can be used to compare one string against another string or against a string literal as shown in the following:

public void paint(Graphics g)
{
    String str1 = new String("abc");

    // create str2 to have the same contents as str1
    String str2 = new String(str1);

    // compare str1 to the string made from it
    if (str1.equals(str2))
        g.drawString("str1 and str2 are equal", 10, 10);

    // compare str1 against a literal
    if (str1.equals("abc"))
        g.drawString("str1 equals abc", 10, 30);
}

In this case str1 is equal to both str2 and the literal abc. To perform a case-insensitive comparison use the equalIgnoreCase method, as follows:

String str1 = new String("abc");
boolean result = str1.equalsIgnoreCase("ABC");

The endsWith method can be used to determine whether a string ends with a given string. Usually more useful are the two startsWith methods. These can be used to determine whether the string starts with a given string or whether that string appears at a specific location within the string. The endsWith and startsWith methods can be used as shown in the following:

// create two Strings
String str1 = new String("My favorite language is Java");
String str2 = new String("I like the Java language");

// see if str1 ends with "Java"
boolean result1 = str1.endsWith("Java");        // true

// see if str1 starts with "My"
boolean result2 = str1.startsWith("My");        // true

// see if starting in offset 11 str2 starts with "Java"
boolean result3 = str2.startsWith("Java", 11);  // true

The two regionMatches methods can be used to see whether a region in one string matches a region in a different string. The second regionMatches method allows for case-insensitive comparisons of this nature. As an example of using regionMatches, consider the following:

// create two longer Strings
String str1 = new String("My favorite language is Java");
String str2 = new String("I like the Java language");

// compare regions
// Start at offset 24 in str1 and compare against offset 11
// in str2 for 4 characters
boolean result = str1.regionMatches(24, str2, 11, 4);    // true

In this case the strings are compared for a length of four characters starting with index 24 in str1 and index 11 in str2. Because each of these substrings is "Java", result is set to true.

Searching Strings

In addition to comparing one string against another, you can also search a string for the occurrence of a character or another string. Unfortunately, you can do only a normal exact character search; no regular expression or wildcard searching is possible. The methods that can be used to search a string are shown in Table 10.6.

Table 10.6. Methods for searching a string.

MethodPurpose
indexOf(int)Searches for the first occurrence of the specified character.
indexOf(int, int)Searches for the first occurrence of the specified character following the given offset.
indexOf(String)Searches for the first occurrence of the specified string.
indexOf(String, int)Searches for the first occurrence of the specified string following the given offset.
lastIndexOf(int)Searches backwards for the last occurrence of the specified character.
lastIndexOf(int, int)Searches backwards for the last occurrence of the specified character preceding the given offset.
lastIndexOf(String)Searches backwards for the last occurrence of the specified string.
lastIndexOf(String, int)Searches backwards for the last occurrence of the specified string preceding the given offset.

As you can see from Table 10.6, the search methods have various signatures but are named either indexOf or lastIndexOf. The indexOf methods search forward from the start of a string to its end; the lastIndexOf methods search in the opposite direction.

To search for a character from the beginning of the string, use the indexOf(int) method. The following code illustrates searching for the character 'Y':

String str = new String("Wish You Were Here");
int index = str.indexOf('Y');

In this case, a 'Y' is found in index 5, so this value is placed in index. This works well for finding the first occurrence of a letter, but what if you need to find subsequent occurrences of the same letter? This can be done by using the indexOf(int, int) method. The additional parameter indicates the offset from which to start the search. By setting this value each time a matching character is found, a loop can be written to easily find all occurrences of a letter. As an example, consider the following code fragment:

String str = new String("Wish You Were Here");

int fromIndex = 0;
while(fromIndex != -1)
{
    fromIndex = str.indexOf('W', fromIndex);
    if (fromIndex != -1)
    {
        // character was matched, use as desired
        fromIndex++;
    }
}

This example starts with str containing "Wish You Were Here" and uses a loop to find all 'W' characters. The variable fromIndex is initially set to 0. This causes the str.indexOf method call to start at the beginning of the string on the first pass through the loop. If fromIndex is not -1, this indicates that the character was found. The value is appended to a results string and fromIndex is incremented to move past the matching character. In this case a W is found at indexes 0 and 9.

Searching for a string is just as simple as searching for a character. The following example will search for the string "er":

public void paint(Graphics g)
{
    String str = new String("Wish You Were Here");
    int count = 0;

    int fromIndex = 0;
    while(fromIndex != -1)
    {
        fromIndex = str.indexOf("er", fromIndex);
        if (fromIndex != -1)
        {
            count++;
            fromIndex++;
        }
    }
    g.drawString(String.valueOf(count), 10, 10);
}

This string will be found twice, once in Were and once in Here.

TIP
The StringTokenizer class provides a very powerful mechanism for parsing a string. This class is fully described in Chapter 11, "The Java Utility Classes."

The StringBuffer Class

The primary limitation of the string class is that once the string is created you cannot change it. If you need to store text that may need to be changed you should use the StringBuffer class. The StringBuffer class includes methods for inserting and appending text. Additionally, a StringBuffer object may be easily converted into a String class when necessary.

To create a new string buffer, you can use any of the three constructors shown in Table 10.7. Examples of using these constructors are as follows:

StringBuffer buf1 = new StringBuffer(25);
StringBuffer buf2 = new StringBuffer();
StringBuffer buf3 = new StringBuffer("This is a StringBuffer");

In the first case, buf1 will be an empty StringBuffer with an initial length of 25. Similarly, buf2 will be an empty StringBuffer but buf3 will contain "This is a StringBuffer".

Table 10.7. StringBuffer constructors.

ConstructorPurpose
StringBuffer()Creates an empty StringBuffer.
StringBuffer(int)Creates an empty StringBuffer with the specified length.
StringBuffer(String)Creates a StringBuffer based on the specified string.

Useful StringBuffer Methods

In addition to the methods for inserting and appending text, the StringBuffer class contains other methods you will need to be familiar with. These are shown in Table 10.8. The capacity method can be used to determine the storage capacity of the string buffer. Because each string buffer can grow as text is appended or inserted, the capacity of the string buffer can exceed the length of the text currently stored in it. While capacity returns the amount of text that could be stored in the currently allocated space of the string buffer, length returns how much of that space is already used. As an example, consider the following:

StringBuffer buf = new StringBuffer(25);
buf.append("13 Characters");
int len = buf.length();
int cap = buf.capacity();

In this case, the constructor specifies a capacity of 25 for buf. However, only thirteen characters are placed in it with the append method. Because of this, the length of buf is 13 while its capacity is 25. These values will be stored in len and cap, respectively.

Table 10.8. Useful methods of the StringBuffer class.

MethodPurpose
capacity()Returns the current capacity of the StringBuffer.
charAt(int)Returns the character located at the specified index.
ensureCapacity(int)Ensures the capacity of the StringBuffer is at least the specified amount.
getChars(int,int,char[],
int)
Copies the specified characters from the StringBuffer into the specified array.
length()Returns the length of the StringBuffer.
setCharAt(int, char)Sets the value at the specified index to the specified character.
setLength(int)Sets the length of the StringBuffer to the specified value.
toStringReturns a string representing the text in the StringBuffer.

The ensureCapacity method can be used to increase the capacity of the string buffer and the setLength method can be used to set the length of the buffer. If setLength is used to reduce the length, the characters at the end of the buffer are lost. If, instead, setLength is used to increase the length of a string buffer, null characters are used to fill the additional space at the end. This can be seen in the following code:

StringBuffer buf = new StringBuffer("0123456789");
buf.setLength(5);
// buf now contains "01234"
buf.setLength(10);
// buf now contains "01234" followed by five null characters

The toString method will be one of the StringBuffer methods you will use most frequently. This method creates a string representation of the text in the string buffer. This is useful because so many of the Java library methods expect a string as a parameter. For example, the following code illustrates how to display the contents of a string buffer in an applet's paint method:

public void paint(Graphics g)
{
    StringBuffer buf = new StringBuffer("Hello, World");
    g.drawString(buf.toString(), 10, 10);
}

The charAt and setCharAt methods can be used to retrieve the character at a specific index and to set the character at a specific index, respectively. This can be seen in the following:

StringBuffer buf = new StringBuffer("Hello");
char ch = buf.charAt(1);
buf.setCharAt(1, 'a');

Because StringBuffers are zero-based, ch will contain 'e'. The setCharAt method replaces this 'e', changing the string buffer to say "Hallo". The getChars method can be used to retrieve characters from a string buffer and place them into a character array. The getChars method has the following signature:

public synchronized void getChars(int srcBegin, int srcEnd,
        char dst[], int dstBegin) ;

The srcEnd parameter indicates the first character after the desired end of the text. It will not be placed in the destination array, so you should be careful to specify the proper value. In the following example, getChars is used to place the characters of String into the array:

StringBuffer buf = new StringBuffer("A String Buffer");
char array[] = new char[10];
buf.getChars(2,8, array, 0);

CAUTION
The getChars method will throw a StringIndexOutOfBoundsException if any of the parameters represents an invalid index.

Appending

Because the main distinction between the String and StringBuffer classes is the capability of a StringBuffer instance to increase in size, methods are provided for appending to a StringBuffer. Each of the following methods can be used to append to a StringBuffer:

Use of these methods is demonstrated by the following:

StringBuffer buf = new StringBuffer("Hello");
buf.append(", World");

StringBuffer buf2 = new StringBuffer("Revolution #");
buf2.append(9);

StringBuffer buf3 = new StringBuffer("My daughter is ");
float ageSavannah = 10F/12F;
buf3.append(ageSavannah);
buf3.append(" years old");

In the first case buf is set to contain "Hello" and then append(String) is used to create "Hello, World". The second case illustrates append(int) to create the text "Revolution #9". Finally, buf3 illustrates the use of append(float) combined with append(String).

Inserting

Of course, sometimes the text you want to add to a string buffer needs to go somewhere other than at the end. In these cases, append is of no use and you need to use one of the provided insert methods. A variety of insert methods is provided, as follows:

Each of these methods is passed the index at which to insert the text and then an object to be inserted. For example, the following code inserts the all-important half month into a child's age:

StringBuffer buf = new StringBuffer("My daughter is 10 months old");
buf.insert(17, "-and-a-half");

Summary

In this chapter, you learned about the String and StringBuffer classes, two of the most commonly used classes in Java programming. You learned that the String class is intended to hold nonchanging text. You saw many examples of using the member methods of the String class, including changing the case of a string, trimming whitespace, and accessing substrings. You learned how to convert variables of other types into strings and how to search and compare strings. Finally, you saw how the StringBuffer class is a more useful class for dynamic strings because text can be inserted and appended.