Chapter 1
IN THIS CHAPTER
Quickly reviewing what you already know about strings
Examining string class methods
Working with substrings
Splitting strings
Using the StringBuilder and StringBuffer classes
Using the CharSequence interface
Strings are among the most common types of objects in Java. Throughout this book are various techniques for working with strings. You’ve seen how to create string variables, how to concatenate strings, and how to compare strings. But so far, I’ve only scratched the surface of what you can do with strings. In this chapter, I dive deeper into what Java can do with strings.
I start with a brief review of what I’ve covered so far about strings, so that you don’t have to go flipping back through the book to find basic information. Then I look at the String
class itself and some of the methods it provides for working with strings. Finally, I examine two almost identical classes named StringBuilder
and StringBuffer
that offer features not available in the basic String
class as well as an interface named CharSequence
that is implemented by String
, StringBuilder
, and StringBuffer
.
To save you the hassle of flipping back through this book, the following paragraphs summarize what I present about strings in earlier chapters:
int
or boolean
. As a result, a string variable holds a reference to an object created from the String
class, not the value of the string itself.String line1 = "Oh what a beautiful morning!";
\n
for new line and \t
for tab. If you want to include a back slash in a string, you must use the escape sequence \\
. Here is a complete list of all the escape sequences you can use:
Escape Sequence |
Explanation |
|
Newline |
|
Tab |
|
Backspace |
|
Carriage return |
|
Form feed |
|
Quotation mark |
|
Back slash |
"a"
is a string literal that happens to be one character long. By contrast, 'a'
is a character literal.+
operator, like this:
String line2 = line1 + "\nOh what a beautiful day!";
+=
operator with strings, like this:
line2 += "\nI've got a beautiful feeling";
int empCount = 50;
String msg = "Number of employees: " + empCount;
integer
and double
) have parse
methods that can convert string values to numeric types. Here’s an example:
String s = "50";
int i = Integer.parseInt(s);
==
). Instead, you should use the equals
method. Here’s an example:
if (lastName.equals("Lowe"))
System.out.println("This is me!");
String
class also has an equalsIgnoreCase
method that compares strings without considering case. Here’s an example:
if (lastName.equalsIgnoreCase("lowe"))
System.out.println("This is me again!");
The String
class is the class you use to create string objects. It has a whole gaggle of methods that are designed to let you find out information about the string that’s represented by the String
class. Table 1-1 lists the most useful of these methods.
TABLE 1-1 String Class Methods
Method |
Description |
|
Returns the character at the specified position in the string. |
|
Compares this string to another string, using alphanumeric order. Returns |
|
Similar to |
|
Returns |
|
Returns |
|
Returns |
|
Similar to |
|
Increases or decreases the indentation of each line within the original string. (Introduced with Java 12.) |
|
Returns the index of the first occurrence of the |
|
Returns the index of the first occurrence of the |
|
Similar to |
|
Returns |
|
Returns the index of the last occurrence of the |
|
Returns the index of the last occurrence of the |
|
Similar to |
|
Returns the length of this string. |
|
Returns a string that repeats the original string the indicated number of times. (Introduced in Java 11.) |
|
Returns a new string that’s based on the original string, but with every occurrence of the first parameter replaced by the second parameter. |
|
Returns a new string that’s based on the original string, but with every occurrence of the first string replaced by the second parameter. Note that the first parameter can be a regular expression. |
|
Returns a new string that’s based on the original string, but with the first occurrence of the first string replaced by the second parameter. Note that the first parameter can be a regular expression. |
|
Splits the string into an array of strings, using the string parameter as a pattern to determine where to split the strings. The string parameter must be a valid regex expression. |
|
Returns |
|
Returns |
|
Returns a copy of the string with all leading and trailing white spaces removed. (Introduced in Java 11.) |
|
Returns a copy of the string with all leading white spaces removed. (Introduced in Java 11.) |
|
Returns a copy of the string with all trailing white spaces removed. (Introduced in Java 11.) |
|
Extracts a substring from this string, beginning at the position indicated by the int parameter and continuing to the end of the string. |
|
Extracts a substring from this string, beginning at the position indicated by the first parameter and ending at the position one character before the value of the second parameter. |
|
Converts the string to an array of individual characters. |
|
Converts the string to lowercase. |
|
Returns the string as a |
|
Converts the string to uppercase. |
|
Returns a copy of the string with all leading and trailing white spaces removed. |
|
A static method that returns a string representation of any primitive type. |
Although you can’t change a string after you create it, you can use methods of the String
class to create new strings that are variations of the original string. The following sections describe some of the more interesting things you can do with these methods.
One of the most basic string operations is determining the length of a string. You do that with the length
method. For example:
String s = "A wonderful day for a neighbor.";
int len = s.length();
Here len
is assigned a value of 31
because the string s
consists of 31 characters.
Getting the length of a string usually isn’t very useful by itself, but the length
method often plays an important role in other string manipulations, as you see throughout the following sections.
Several of the methods of the String
class return modified versions of the original string. toLowerCase
, for example, converts a string to all-lowercase letters:
String s1 = "Oompa Loompa";
String s2 = s1.toLowerCase();
Here s2
is set to the string oompa loompa
. The toUpperCase
method works the same way but converts strings to all-uppercase letters.
The trim
method removes white-space characters (spaces, tabs, newlines, and so on) from the start and end of a word. Here’s an example:
String s = " Oompa Loompa ";
s = s.trim();
Here the spaces before and after Oompa Loompa
are removed. Thus, the resulting string is 12 characters long.
s.trim();
Here the trim
method trims the string — but then the program discards the result. The remedy is to assign the result of this expression back to s
, like this:
s = s.trim();
You can use the charAt
method to extract a character from a specific position in a string. When you do, keep in mind that the index number for the first character in a string is 0
, not 1
. Also, you should check the length of the string before extracting a character. If you specify an index value that’s beyond the end of the string, the exception StringIndexOutOfBoundsException
is thrown. (Fortunately, this is an unchecked exception, so you don’t have to enclose the charAt
method in a try/catch
statement.)
Here’s an example of a program that uses the charAt
method to count the number of vowels in a string entered by the user:
import java.util.Scanner;
public class CountVowels
{
static Scanner sc = new Scanner(System.in);
public static void main(String[] args)
{
System.out.print("Enter a string: ");
String s = sc.nextLine();
int vowelCount = 0;
for (int i = 0; i < s.length(); i++)
{
char c = s.charAt(i);
if ( (c == 'A') || (c == 'a')
|| (c == 'E') || (c == 'e')
|| (c == 'I') || (c == 'i')
|| (c == 'O') || (c == 'o')
|| (c == 'U') || (c == 'u') )
vowelCount++;
}
System.out.println("That string contains "
+ vowelCount + " vowels.");
}
}
Here the for
loop checks the length of the string to make sure that the index variable i
doesn’t exceed the string length. Then each character is extracted and checked with an if
statement to see whether it is a vowel. The condition expression in this if
statement is a little complicated because it must check for five different vowels, both uppercase and lowercase.
Following is an example that shows the output created by this program if you enter a string listing all of the letters of the alphabet:
Enter a string: abcdefghijklmnopqrstuvwxyz
That string contains 5 vowels.
The substring
method lets you extract a portion of a string. This method has two forms. The first version accepts a single integer parameter. It returns the substring that starts at the position indicated by this parameter and extends to the rest of the string. (Remember that string positions start with 0
, not 1
.) Here’s an example:
String s = "Baseball";
String b = s.substring(4); // "ball"
Here b
is assigned the string ball
.
The second version of the substring
method accepts two parameters to indicate the start and end of the substring you want to extract. Note that the substring actually ends at the character that’s immediately before the position indicated by the second parameter. So, to extract the characters at positions 2
through 5
, specify 2
as the start position and 6
as the ending position. For example:
String s = "Baseball";
String b = s.substring(2, 6); // "seba"
Here b
is assigned the string seba
.
The following program uses substrings to replace all the vowels in a string entered by the user with asterisks:
import java.util.Scanner;
public class MarkVowels
{
static Scanner sc = new Scanner(System.in);
public static void main(String[] args)
{
System.out.print("Enter a string: ");
String s = sc.nextLine();
String originalString = s;
int vowelCount = 0;
for (int i = 0; i < s.length(); i++)
{
char c = s.charAt(i);
if ( (c == 'A') || (c == 'a')
|| (c == 'E') || (c == 'e')
|| (c == 'I') || (c == 'i')
|| (c == 'O') || (c == 'o')
|| (c == 'U') || (c == 'u') )
{
String front = s.substring(0, i);
String back = s.substring(i+1);
s = front + "*" + back;
}
}
System.out.println();
System.out.println(originalString);
System.out.println(s);
}
}
This program uses a for
loop and the charAt
method to extract each character from the string. Then, if the character is a vowel, a string named front
is created that consists of all the characters that appear before the vowel. Next, a second string named back
is created with all the characters that appear after the vowel. Finally, the s
string is replaced by a new string that’s constructed from the front
string, an asterisk, and the back
string.
Here’s some sample console output from this program so that you can see how it works:
Enter a string: Where have all the vowels gone?
Where have all the vowels gone?
Wh*r* h*v* *ll th* v*w*ls g*n*?
The split
command is especially useful for splitting a string into separate strings based on a delimiter character. Suppose you have a string with the parts of an address separated by colons, like this:
1500 N. Third Street:Fresno:CA:93722
With the split
method, you can easily separate this string into four strings. In the process, the colons are discarded.
The split
method carves a string into an array of strings separated by the delimiter character passed via a string parameter. Here’s a routine that splits an address into separate strings and then prints out all the strings:
String address =
"1500 N. Third Street:Fresno:CA:93722";
String[] parts = address.split(":");
for (int i = 0; i < parts.length; i++)
System.out.println(parts[i]);
If you run this code, the following lines are displayed on the console:
1500 N. Third Street
Fresno
CA
93722
The string passed to the split
method is actually a special type of string used for pattern recognition, called a regular expression. You discover regular expressions in Book 5. For now, here are a few regular expressions that might be useful when you use the split
method:
Regular Expression |
Explanation |
|
A tab character |
|
A newline character |
|
A vertical bar |
|
Any white-space character |
|
One or more occurrences of any white-space character |
The last regular expression in this table, \\s+
, is especially useful for breaking a string into separate words. The following program accepts a string from the user, breaks it into separate words, and then displays the words on separate lines:
import java.util.Scanner;
public class ListWords
{
static Scanner sc = new Scanner(System.in);
public static void main(String[] args)
{
System.out.print("Enter a string: ");
String s = sc.nextLine();
String[] word = s.split("\\s+");
for (String w : word)
System.out.println(w);
}
}
Here’s a sample of the console output for a typical execution of this program:
Enter a string: This string has several words
This
string
has
several
words
Notice that some of the words in the string entered by the user are preceded by more than one space character. The \\s+
pattern used by the split
method treats any consecutive white-space character as a single delimiter when splitting the words.
You can use the replaceFirst
or replaceAll
method to replace a part of a string that matches a pattern you supply with some other text. Here’s the main
method of a program that gets a line of text from the user and then replaces all occurrences of the string cat
with dog
:
public static void main(String[] args)
{
Scanner sc = new Scanner(System.in);
System.out.print("Enter a string: ");
String s = sc.nextLine();
s = s.replaceAll("cat", "dog");
System.out.println(s);
}
And here’s the console for a typical execution of this program:
Enter a string: I love cats.
I love dogs.
As with the split
methods, the first parameter of replace
methods can be a regular expression that provides a complex matching string. (For more information, see Book 5, Chapter 3.)
The String
class is powerful, but it’s not very efficient for programs that require heavy-duty string manipulation. Because String
objects are immutable, any method of the String
class that modifies the string in any way must create a new String
object and copy the modified contents of the original string object to the new string. That’s not so bad if it happens only occasionally, but it can be inefficient in programs that do it a lot.
Even string concatenation is inherently inefficient. Consider these statements:
int count = 5;
String msg = "There are ";
msg += count;
msg += " apples in the basket.";
These four statements may actually create five String
objects:
"There are "
: Created for the literal in the second statement. The msg
variable is assigned a reference to this string."5"
: Created to hold the result of count.toString()
. The toString
method is implicitly called by the third statement, so count
is concatenated with msg
."There are 5"
: Created as a result of the concatenation in the third statement. A reference to this object is assigned to msg
."apples in the basket."
: Created to hold the literal in the fourth statement."There are 5 apples in the basket."
: Created to hold the result of the concatenation in the fourth statement. A reference to this object is assigned to msg
.For programs that do only occasional string concatenation and simple string manipulations, these inefficiencies aren't a big deal. In fact, the compiler may optimize them to eliminate the inefficiency. But for programs that do extensive string manipulation, Java offers two alternatives to the String
class: the StringBuilder
and StringBuffer
classes.
You can’t assign string literals directly to a StringBuilder
object, as you can with a String
object. The StringBuilder
class, however, has a constructor that accepts a String
as a parameter. So to create a StringBuilder
object, you use a statement such as this:
StringBuilder sb = new StringBuilder("Today is the day!");
Internally, a StringBuilder
object maintains a fixed area of memory where it stores a string value. This area of memory is called the buffer. The string held in this buffer doesn’t have to use the entire buffer. As a result, a StringBuilder
object has both a length and a capacity. The length represents the current length of the string maintained by the StringBuilder
, and the capacity represents the size of the buffer itself. Note that the length can’t exceed the capacity.
When you create a StringBuilder
object, initially the capacity is set to the length of the string plus 16. The StringBuilder
class automatically increases its capacity whenever necessary, so you don’t have to worry about exceeding the capacity.
Table 1-2 lists the most useful methods of the StringBuilder
class. Note that the StringBuffer
class uses the same methods. If you have to use StringBuffer
instead of StringBuilder
, just change the class name and use the same methods.
TABLE 1-2 StringBuilder Methods
Method |
Description |
|
Appends the string representation of the primitive type to the end of the string. |
|
Calls the object’s |
|
Appends the string to the end of the |
|
Returns the character at the specified position in the string. |
|
Deletes characters starting with the first |
|
Deletes the character at the specified position. |
|
Ensures that the capacity of |
|
Returns the capacity of this |
|
Returns the index of the first occurrence of the specified string. If the string doesn’t appear, returns |
|
Returns the index of the first occurrence of the specified string, starting the search at the specified index position. If the string doesn’t appear, returns |
|
Inserts the string representation of the primitive type at the point specified by the |
|
Calls the |
|
Inserts the string at the point specified by the |
|
Returns the index of the last occurrence of the specified string. If the string doesn’t appear, returns |
|
Returns the index of the last occurrence of the specified string, starting the search at the specified index position. If the string doesn’t appear, returns |
|
Returns the length of this string. |
|
Replaces the substring indicated by the first two parameters with the string provided by the third parameter. |
|
Reverses the order of characters. |
|
Sets the character at the specified position to the specified character. |
|
Sets the length of the string. If that length is less than the current length, the string is truncated; if it’s greater than the current length, new characters — hexadecimal zeros — are added. |
|
Extracts a substring, beginning at the position indicated by the |
|
Extracts a substring, beginning at the position indicated by the first parameter and ending at the position one character before the value of the second parameter. |
|
Returns the current value as a |
|
Reduces the capacity of the |
To illustrate how the StringBuilder
class works, here’s a StringBuilder
version of the MarkVowels
program from earlier in this chapter:
import java.util.Scanner;
public class StringBuilderApp
{
static Scanner sc = new Scanner(System.in);
public static void main(String[] args)
{
System.out.print("Enter a string: ");
String s = sc.nextLine();
StringBuilder sb = new StringBuilder(s);
int vowelCount = 0;
for (int i = 0; i < s.length(); i++)
{
char c = s.charAt(i);
if ( (c == 'A') || (c == 'a')
|| (c == 'E') || (c == 'e')
|| (c == 'I') || (c == 'i')
|| (c == 'O') || (c == 'o')
|| (c == 'U') || (c == 'u') )
{
sb.setCharAt(i, '*');
}
}
System.out.println();
System.out.println(s);
System.out.println(sb.toString());
}
}
This program uses the setCharAt
method to directly replace any vowels it finds with asterisks. That’s much more efficient than concatenating substrings (which is the way the String
version of this program worked).
The Java API includes a useful interface called CharSequence
. All three of the classes discussed in this chapter — String
, StringBuilder
, and StringBuffer
— implement this interface. This method exists primarily to let you use String
, StringBuilder
, and StringBuffer
interchangeably.
Toward that end, several of the methods of the String
, StringBuilder
, and StringBuffer
classes use CharSequence
as a parameter type. For those methods, you can pass a String
, StringBuilder
, or StringBuffer
object. Note that a string literal is treated as a String
object, so you can use a string literal anywhere a CharSequence
is called for.
char charAt(int)
: Returns the character at the specified position.int length()
: Returns the length of the sequence.subSequence(int start, int end)
: Returns the substring indicated by the start and end parameters.toString()
: Returns a String
representation of the sequence.If you're inclined to use CharSequence
as a parameter type for a method so that the method works with a String
, StringBuilder
, or StringBuffer
object, be advised that you can use only these four methods.