Strings
Strings are a primitive data-type in many programming languages. In Java a String is an object because it can be represented as an array of the char primitive type. For instance, the String "hello" could be represented as follows:
char[] s1 = {'h','e','l','l','o'};
Obviously this is rather painful, and is why Java supports Strings as a built-in type:
String s1 = "hello";
Strings in Java are immutable: once they are constructed they can never be changed. This may sound implausible, given the following valid lines of Java:
String s1 = "Hello";
s1 = s1 + " World";
Although it is a subtle point (and will be explained in great detail in the chapters to come), no Strings have been modified in this example. The only thing changing in this example is the underlying String that the variable s1 refers to.
The variable s1 starts out referring to a String object with the value "Hello". On the second line, Java first constructs a new String object with the value " World". Next, when the concatenation is performed, Java creates a third String object with the value "Hello World", and points the variable s1 at this.
Because Strings are objects, they support methods. It is important to realize that although these methods perform operations with the value of the String, due to the fact that Strings are immutable, they never change the value of the String – instead they construct new Strings:
package strings;
public class Strings {
public static void main(String[] args) {
String s1 = "Hello World";
System.out.println(s1.toUpperCase());
System.out.println(s1.substring(6));
System.out.println(s1.indexOf("W"));
}
}
The program above prints out:
HELLO WORLD
World
6
Some of the more useful methods supported by String are as follows:
• length: this returns the number of characters in the String
• matches: this tests whether the String matches a given regular expression. Regular expressions are outside the scope of this book, but are a standard way of expressing textual patterns
• replaceAll and replaceFirst: replace instances of a specified String with a new String
• split: splits the String based on a delimiter (for example, a comma) and returns an array of Strings
• substring: returns a portion of the String based on a starting and (optionally) end index. As with arrays, the first character in the String is at position 0
• trim: removes leading and trailing whitespace from the String
StringBuilders are similar to Strings, except they are mutable: A StringBuilder is an object, and encapsulates a string of text, but this string can be modified after the StringBuilder has been created:
package strings;
public class StringBuilder {
public static void main(String[] args) {
StringBuilder sb1 = new StringBuilder ("Hello");
sb1.append(" World!!!");
sb1.reverse();
System.out.println(sb1);
}
}
This produces:
!!!dlroW olleH
The main reason StringBuilders are used is due to their performance advantage over Strings when concatenating many values together. String concatenation is a relatively common task, and there are cases when hundreds or thousands of strings must be concatenated together. In these cases the append method on StringBuilder will generally offer superior performance to string concatenation using the + operator.
There is a similar class to StringBuilder called StringBuffer. These two classes are essentially the same except StringBuilder is not thread safe. Do not worry if you do not understand that concept yet, it will be explained in the Threading chapter. As a general rule StringBuilder offers superior performance to StringBuffer.
Unless I am concatenating a large number of strings together I generally prefer the simplicity of Strings. The speed of modern computers means it is seldom worth optimizing relatively simple operations when there is a trade-off in terms of code simplicity.