Certified Core Java Developer Learning Resources Strings and StringBuffer

Learning Resources
 

Strings,StringBuffer

Strings

Strings, which are widely used in Java programming, are a sequence of characters. In the Java programming language, strings are objects.

The Java platform provides the Stringclass to create and manipulate strings.

Creating Strings

The most direct way to create a string is to write:

String greeting = "Hello world!";

In this case, "Hello world!" is a string literal—a series of characters in your code that is enclosed in double quotes. Whenever it encounters a string literal in your code, the compiler creates a Stringobject with its value—in this case, Hello world!.

As with any other object, you can create Stringobjects by using the newkeyword and a constructor. The Stringclass has thirteen constructors that allow you to provide the initial value of the string using different sources, such as an array of characters:

char[] helloArray = { 'h', 'e', 'l', 'l', 'o', '.' };
String helloString = new String(helloArray);
System.out.println(helloString);

The last line of this code snippet displays hello.


Note: The Stringclass is immutable, so that once it is created a Stringobject cannot be changed. The Stringclass has a number of methods, some of which will be discussed below, that appear to modify strings. Since strings are immutable, what these methods really do is create and return a new string that contains the result of the operation.

String Length

Methods used to obtain information about an object are known as accessor methods. One accessor method that you can use with strings is the length()method, which returns the number of characters contained in the string object. After the following two lines of code have been executed, lenequals 17:

String palindrome = "Dot saw I was Tod";
int len = palindrome.length();

A palindrome is a word or sentence that is symmetric—it is spelled the same forward and backward, ignoring case and punctuation. Here is a short and inefficient program to reverse a palindrome string. It invokes the Stringmethod charAt(i), which returns the ith character in the string, counting from 0.


public class StringDemo {
    public static void main(String[] args) {
        String palindrome = "Dot saw I was Tod";
        int len = palindrome.length();
        char[] tempCharArray = new char[len];
        char[] charArray = new char[len];
        
        // put original string in an 
        // array of chars
        for (int i = 0; i < len; i++) {
            tempCharArray[i] = 
                palindrome.charAt(i);
        } 
        
        // reverse array of chars
        for (int j = 0; j < len; j++) {
            charArray[j] =
                tempCharArray[len - 1 - j];
        }
        
        String reversePalindrome =
            new String(charArray);
        System.out.println(reversePalindrome);
    }
}

Running the program produces this output:

doT saw I was toD

To accomplish the string reversal, the program had to convert the string to an array of characters (first forloop), reverse the array into a second array (second forloop), and then convert back to a string. The Stringclass includes a method, getChars(), to convert a string, or a portion of a string, into an array of characters so we could replace the first forloop in the program above with

palindrome.getChars(0, len, tempCharArray, 0);

Concatenating Strings

The Stringclass includes a method for concatenating two strings:

string1.concat(string2); 

This returns a new string that is string1 with string2 added to it at the end.

You can also use the concat()method with string literals, as in:

"My name is ".concat("Rumplestiltskin");

Strings are more commonly concatenated with the + operator, as in

"Hello," + " world" + "!"

which results in

"Hello, world!"

The + operator is widely used in printstatements. For example:

String string1 = "saw I was ";
System.out.println("Dot " + string1 + "Tod");

which prints

Dot saw I was Tod

Such a concatenation can be a mixture of any objects. For each object that is not a String, its toString()method is called to convert it to a String.


Note: The Java programming language does not permit literal strings to span lines in source files, so you must use the +concatenation operator at the end of each line in a multi-line string. For example:
String quote = 
    "Now is the time for all good " +
    "men to come to the aid of their country.";

Breaking strings between lines using the +concatenation operator is, once again, very common in printstatements.


Creating Format Strings

You have seen the use of the printf()and format()methods to print output with formatted numbers. The Stringclass has an equivalent class method, format(), that returns a Stringobject rather than a PrintStreamobject.

Using String'sstatic format()method allows you to create a formatted string that you can reuse, as opposed to a one-time print statement. For example, instead of

System.out.printf("The value of the float " +
                  "variable is %f, while " +
                  "the value of the " + 
                  "integer variable is %d, " +
                  "and the string is %s", 
                  floatVar, intVar, stringVar); 

you can write

String fs;
fs = String.format("The value of the float " +
                   "variable is %f, while " +
                   "the value of the " + 
                   "integer variable is %d, " +
                   " and the string is %s",
                   floatVar, intVar, stringVar);
System.out.println(fs);

Converting Between Numbers and Strings

Converting Strings to Numbers

Frequently, a program ends up with numeric data in a string object—a value entered by the user, for example.

The Numbersubclasses that wrap primitive numeric types ( Byte, Integer, Double, Float, Long, and Short) each provide a class method named valueOfthat converts a string to an object of that type. Here is an example, ValueOfDemo, that gets two strings from the command line, converts them to numbers, and performs arithmetic operations on the values:


public class ValueOfDemo {
    public static void main(String[] args) {

        // this program requires two 
        // arguments on the command line 
        if (args.length == 2) {
            // convert strings to numbers
            float a = (Float.valueOf(args[0])).floatValue(); 
            float b = (Float.valueOf(args[1])).floatValue();

            // do some arithmetic
            System.out.println("a + b = " +
                               (a + b));
            System.out.println("a - b = " +
                               (a - b));
            System.out.println("a * b = " +
                               (a * b));
            System.out.println("a / b = " +
                               (a / b));
            System.out.println("a % b = " +
                               (a % b));
        } else {
            System.out.println("This program " +
                "requires two command-line arguments.");
        }
    }
}

The following is the output from the program when you use 4.5and 87.2for the command-line arguments:

a + b = 91.7
a - b = -82.7
a * b = 392.4
a / b = 0.0516055
a % b = 4.5

Note: Each of the Numbersubclasses that wrap primitive numeric types also provides a parseXXXX()method (for example, parseFloat()) that can be used to convert strings to primitive numbers. Since a primitive type is returned instead of an object, the parseFloat()method is more direct than the valueOf()method. For example, in the ValueOfDemoprogram, we could use:
float a = Float.parseFloat(args[0]);
float b = Float.parseFloat(args[1]);

Converting Numbers to Strings

Sometimes you need to convert a number to a string because you need to operate on the value in its string form. There are several easy ways to convert a number to a string:

int i;
// Concatenate "i" with an empty string; conversion is handled for you.
String s1 = "" + i;

or

// The valueOf class method.
String s2 = String.valueOf(i);

Each of the Numbersubclasses includes a class method, toString(), that will convert its primitive type to a string. For example:

int i;
double d;
String s3 = Integer.toString(i); 
String s4 = Double.toString(d); 

The ToStringDemoexample uses the toStringmethod to convert a number to a string. The program then uses some string methods to compute the number of digits before and after the decimal point:


public class ToStringDemo {
    
    public static void main(String[] args) {
        double d = 858.48;
        String s = Double.toString(d);
        
        int dot = s.indexOf('.');
        
        System.out.println(dot + " digits " +
            "before decimal point.");
        System.out.println( (s.length() - dot - 1) +
            " digits after decimal point.");
    }
}

The output of this program is:

3 digits before decimal point.
2 digits after decimal point.

Manipulating Characters in a String

The Stringclass has a number of methods for examining the contents of strings, finding characters or substrings within a string, changing case, and other tasks.

Getting Characters and Substrings by Index

You can get the character at a particular index within a string by invoking the charAt()accessor method. The index of the first character is 0, while the index of the last character is length()-1. For example, the following code gets the character at index 9 in a string:

String anotherPalindrome = "Niagara. O roar again!"; 
char aChar = anotherPalindrome.charAt(9);

Indices begin at 0, so the character at index 9 is 'O', as illustrated in the following figure:

Use the charAt method to get a character at a particular index.

If you want to get more than one consecutive character from a string, you can use the substringmethod. The substringmethod has two versions, as shown in the following table:

The substringMethods in the StringClass
Method Description
String substring(int beginIndex, int endIndex) Returns a new string that is a substring of this string. The first integer argument specifies the index of the first character. The second integer argument is the index of the last character - 1.
String substring(int beginIndex) Returns a new string that is a substring of this string. The integer argument specifies the index of the first character. Here, the returned substring extends to the end of the original string.

The following code gets from the Niagara palindrome the substring that extends from index 11 up to, but not including, index 15, which is the word "roar":

String anotherPalindrome = "Niagara. O roar again!"; 
String roar = anotherPalindrome.substring(11, 15); 
Use the substring method to get part of a string.

Other Methods for Manipulating Strings

Here are several other Stringmethods for manipulating strings:

Other Methods in the StringClass for Manipulating Strings
Method Description
String[] split(String regex)String[] split(String regex, int limit) Searches for a match as specified by the string argument (which contains a regular expression) and splits this string into an array of strings accordingly. The optional integer argument specifies the maximum size of the returned array. Regular expressions are covered in the lesson titled "Regular Expressions."
CharSequence subSequence(int beginIndex, int endIndex) Returns a new character sequence constructed from beginIndexindex up until endIndex- 1.
String trim() Returns a copy of this string with leading and trailing white space removed.
String toLowerCase()
String toUpperCase()
Returns a copy of this string converted to lowercase or uppercase. If no conversions are necessary, these methods return the original string.

Searching for Characters and Substrings in a String

Here are some other Stringmethods for finding characters or substrings within a string. The Stringclass provides accessor methods that return the position within the string of a specific character or substring: indexOf()and lastIndexOf(). The indexOf()methods search forward from the beginning of the string, and the lastIndexOf()methods search backward from the end of the string. If a character or substring is not found, indexOf()and lastIndexOf()return -1.

The Stringclass also provides a search method, contains, that returns true if the string contains a particular character sequence. Use this method when you only need to know that the string contains a character sequence, but the precise location isn't important.

The following table describes the various string search methods.

The Search Methods in the StringClass
Method Description
int indexOf(int ch)
int lastIndexOf(int ch)
Returns the index of the first (last) occurrence of the specified character.
int indexOf(int ch, int fromIndex)
int lastIndexOf(int ch, int fromIndex)
Returns the index of the first (last) occurrence of the specified character, searching forward (backward) from the specified index.
int indexOf(String str)
int lastIndexOf(String str)
Returns the index of the first (last) occurrence of the specified substring.
int indexOf(String str, int fromIndex)
int lastIndexOf(String str, int fromIndex)
Returns the index of the first (last) occurrence of the specified substring, searching forward (backward) from the specified index.
boolean contains(CharSequence s) Returns true if the string contains the specified character sequence.

Note: CharSequenceis an interface that is implemented by the Stringclass. Therefore, you can use a string as an argument for the contains()method.

Replacing Characters and Substrings into a String

The Stringclass has very few methods for inserting characters or substrings into a string. In general, they are not needed: You can create a new string by concatenation of substrings you have removed from a string with the substring that you want to insert.

The Stringclass does have four methods for replacing found characters or substrings, however. They are:

Methods in the StringClass for Manipulating Strings
Method Description
String replace(char oldChar, char newChar) Returns a new string resulting from replacing all occurrences of oldChar in this string with newChar.
String replace(CharSequence target, CharSequence replacement) Replaces each substring of this string that matches the literal target sequence with the specified literal replacement sequence.
String replaceAll(String regex, String replacement) Replaces each substring of this string that matches the given regular expression with the given replacement.
String replaceFirst(String regex, String replacement) Replaces the first substring of this string that matches the given regular expression with the given replacement.

An Example

The following class, Filename, illustrates the use of lastIndexOf()and substring()to isolate different parts of a file name.


Note: The methods in the following Filenameclass don't do any error checking and assume that their argument contains a full directory path and a filename with an extension. If these methods were production code, they would verify that their arguments were properly constructed.

public class Filename {
    private String fullPath;
    private char pathSeparator, 
                 extensionSeparator;

    public Filename(String str, char sep, char ext) {
        fullPath = str;
        pathSeparator = sep;
        extensionSeparator = ext;
    }

    public String extension() {
        int dot = fullPath.lastIndexOf(extensionSeparator);
        return fullPath.substring(dot + 1);
    }

    // gets filename without extension
    public String filename() {
        int dot = fullPath.lastIndexOf(extensionSeparator);
        int sep = fullPath.lastIndexOf(pathSeparator);
        return fullPath.substring(sep + 1, dot);
    }

    public String path() {
        int sep = fullPath.lastIndexOf(pathSeparator);
        return fullPath.substring(0, sep);
    }
}

Here is a program, FilenameDemo, that constructs a Filenameobject and calls all of its methods:


public class FilenameDemo {
    public static void main(String[] args) {
        final String FPATH = "/home/user/index.html";
        Filename myHomePage = new Filename(FPATH, '/', '.');
        System.out.println("Extension = " + myHomePage.extension());
        System.out.println("Filename = " + myHomePage.filename());
        System.out.println("Path = " + myHomePage.path());
    }
}

And here's the output from the program:

Extension = html
Filename = index
Path = /home/user

As shown in the following figure, our extensionmethod uses lastIndexOfto locate the last occurrence of the period (.) in the file name. Then substringuses the return value of lastIndexOfto extract the file name extension — that is, the substring from the period to the end of the string. This code assumes that the file name has a period in it; if the file name does not have a period, lastIndexOfreturns -1, and the substring method throws a StringIndexOutOfBoundsException.

The use of lastIndexOf and substring in the extension method in the Filename class.

Also, notice that the extensionmethod uses dot + 1as the argument to substring. If the period character (.) is the last character of the string, dot + 1is equal to the length of the string, which is one larger than the largest index into the string (because indices start at 0). This is a legal argument to substringbecause that method accepts an index equal to, but not greater than, the length of the string and interprets it to mean "the end of the string."

Comparing Strings and Portions of Strings

The Stringclass has a number of methods for comparing strings and portions of strings. The following table lists these methods.

Methods for Comparing Strings
Method Description
boolean endsWith(String suffix)
boolean startsWith(String prefix)
Returns trueif this string ends with or begins with the substring specified as an argument to the method.
boolean startsWith(String prefix, int offset) Considers the string beginning at the index offset, and returns trueif it begins with the substring specified as an argument.
int compareTo(String anotherString) Compares two strings lexicographically. Returns an integer indicating whether this string is greater than (result is > 0), equal to (result is = 0), or less than (result is < 0) the argument.
int compareToIgnoreCase(String str) Compares two strings lexicographically, ignoring differences in case. Returns an integer indicating whether this string is greater than (result is > 0), equal to (result is = 0), or less than (result is < 0) the argument.
boolean equals(Object anObject) Returns trueif and only if the argument is a Stringobject that represents the same sequence of characters as this object.
boolean equalsIgnoreCase(String anotherString) Returns trueif and only if the argument is a Stringobject that represents the same sequence of characters as this object, ignoring differences in case.
boolean regionMatches(int toffset, String other, int ooffset, int len) Tests whether the specified region of this string matches the specified region of the String argument.

Region is of length lenand begins at the index toffsetfor this string and ooffsetfor the other string.

boolean regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len) Tests whether the specified region of this string matches the specified region of the String argument.

Region is of length lenand begins at the index toffsetfor this string and ooffsetfor the other string.

The boolean argument indicates whether case should be ignored; if true, case is ignored when comparing characters.

boolean matches(String regex) Tests whether this string matches the specified regular expression. Regular expressions are discussed in the lesson titled "Regular Expressions."

The following program, RegionMatchesDemo, uses the regionMatchesmethod to search for a string within another string:


public class RegionMatchesDemo {
    public static void main(String[] args) {
        String searchMe = "Green Eggs and Ham";
        String findMe = "Eggs";
        int searchMeLength = searchMe.length();
        int findMeLength = findMe.length();
        boolean foundIt = false;
        for (int i = 0; 
             i <= (searchMeLength - findMeLength);
             i++) {
           if (searchMe.regionMatches(i, findMe, 0, findMeLength)) {
              foundIt = true;
              System.out.println(searchMe.substring(i, i + findMeLength));
              break;
           }
        }
        if (!foundIt)
            System.out.println("No match found.");
    }
}

The output from this program is Eggs.

The program steps through the string referred to by searchMeone character at a time. For each character, the program calls the regionMatches method to determine whether the substring beginning with the current character matches the string the program is looking for.

Class StringBuffer

java.lang.Object
  
extended by
java.lang.StringBuffer
All Implemented Interfaces:
CharSequence, Serializable

public final class StringBuffer
extends Object
implements Serializable, CharSequence

A string buffer implements a mutable sequence of characters. A string buffer is like a String, but can be modified. At any point in time it contains some particular sequence of characters, but the length and content of the sequence can be changed through certain method calls.

String buffers are safe for use by multiple threads. The methods are synchronized where necessary so that all the operations on any particular instance behave as if they occur in some serial order that is consistent with the order of the method calls made by each of the individual threads involved.

String buffers are used by the compiler to implement the binary string concatenation operator +. For example, the code:

     x = "a" + 4 + "c"
 

is compiled to the equivalent of:

     x = new StringBuffer().append("a").append(4).append("c")
                           .toString()
 
which creates a new string buffer (initially empty), appends the string representation of each operand to the string buffer in turn, and then converts the contents of the string buffer to a string. Overall, this avoids creating many temporary strings.

The principal operations on a StringBufferare the appendand insertmethods, which are overloaded so as to accept data of any type. Each effectively converts a given datum to a string and then appends or inserts the characters of that string to the string buffer. The appendmethod always adds these characters at the end of the buffer; the insertmethod adds the characters at a specified point.

For example, if zrefers to a string buffer object whose current contents are "start", then the method call z.append("le")would cause the string buffer to contain "startle", whereas z.insert(4, "le")would alter the string buffer to contain "starlet".

In general, if sb refers to an instance of a StringBuffer, then sb.append(x)has the same effect as sb.insert(sb.length(), x).

Every string buffer has a capacity. As long as the length of the character sequence contained in the string buffer does not exceed the capacity, it is not necessary to allocate a new internal buffer array. If the internal buffer overflows, it is automatically made larger.

 

StringBuffer versus String

Java provides the StringBufferand Stringclasses, and the Stringclass is used to manipulate character strings that cannot be changed. Simply stated, objects of type Stringare read only and immutable. The StringBufferclass is used to represent characters that can be modified.
 

The significant performance difference between these two classes is that StringBufferis faster than Stringwhen performing simple concatenations. In Stringmanipulation code, character strings are routinely concatenated. Using the Stringclass, concatenations are typically performed as follows:

     String str = new String ("Stanford  ");
     str += "Lost!!";


If you were to use StringBufferto perform the same concatenation, you would need code that looks like this:

     StringBuffer str = new StringBuffer ("Stanford ");
     str.append("Lost!!");


Developers usually assume that the first example above is more efficient because they think that the second example, which uses the appendmethod for concatenation, is more costly than the first example, which uses the +operator to concatenate two Stringobjects.

The +operator appears innocent, but the code generated produces some surprises. Using a StringBufferfor concatenation can in fact produce code that is significantly faster than using a String. To discover why this is the case, we must examine the generated bytecode from our two examples. The bytecode for the example using Stringlooks like this:

0 new #7 
3 dup 
4 ldc #2 
6 invokespecial #12 
9 astore_1
10 new #8 
13 dup
14 aload_1
15 invokestatic #23 
18 invokespecial #13 
21 ldc #1 
23 invokevirtual #15 
26 invokevirtual #22 
29 astore_1


The bytecode at locations 0 through 9 is executed for the first line of code, namely:

     String str = new String("Stanford ");


Then, the bytecode at location 10 through 29 is executed for the concatenation:

     str += "Lost!!";


Things get interesting here. The bytecode generated for the concatenation creates a StringBufferobject, then invokes its appendmethod: the temporary StringBufferobject is created at location 10, and its appendmethod is called at location 23. Because the Stringclass is immutable, a StringBuffermust be used for concatenation.

After the concatenation is performed on the StringBufferobject, it must be converted back into a String. This is done with the call to the toStringmethod at location 26. This method creates a new Stringobject from the temporary StringBufferobject. The creation of this temporary StringBufferobject and its subsequent conversion back into a Stringobject are very expensive.

In summary, the two lines of code above result in the creation of three objects:

  1. A Stringobject at location 0
  2. A StringBufferobject at location 10
  3. A Stringobject at location 26


Now, let's look at the bytecode generated for the example using StringBuffer:

0 new #8 
3 dup
4 ldc #2 
6 invokespecial #13 
9 astore_1
10 aload_1 
11 ldc #1 
13 invokevirtual #15 
16 pop


The bytecode at locations 0 to 9 is executed for the first line of code:

     StringBuffer str = new StringBuffer("Stanford ");


The bytecode at location 10 to 16 is then executed for the concatenation:

     str.append("Lost!!");


Notice that, as is the case in the first example, this code invokes the appendmethod of a StringBufferobject. Unlike the first example, however, there is no need to create a temporary StringBufferand then convert it into a Stringobject. This code creates only one object, the StringBuffer, at location 0.

In conclusion, StringBufferconcatenation is significantly faster than Stringconcatenation. Obviously, StringBuffers should be used in this type of operation when possible. If the functionality of the Stringclass is desired, consider using a StringBufferfor concatenation and then performing one conversion to String.

 

 For Support