Chapter 8 Strings and things8.1 CharactersIn Java and other object-oriented languages, objects are collections of related data that come with a set of methods. These methods operate on the objects, performing computations and sometimes modifying the object’s data. Strings are objects, so you might ask “What is the data contained in a String object?” and “What are the methods we can invoke on String objects?” The components of a String object are letters or, more generally, characters. Not all characters are letters; some are numbers, symbols, and other things. For simplicity I call them all letters. There are many methods, but I use only a few in this book. The rest are documented at http://download.oracle.com/javase/6/docs/api/java/lang/String.html. The first method we will look at is charAt, which allows you to extract letters from a String. char is the variable type that can store individual characters (as opposed to strings of them). chars work just like the other types we have seen: char ltr = 'c';
if (ltr == 'c') {
System.out.println(ltr);
} Character values appear in single quotes, like ’c’. Unlike string values (which appear in double quotes), character values can contain only a single letter or symbol. Here’s how the charAt method is used: String fruit = "banana";
char letter = fruit.charAt(1);
System.out.println(letter); fruit.charAt() means that I am invoking the charAt method on the object named fruit. I am passing the argument 1 to this method, which means that I want to know the first letter of the string. The result is a character, which is stored in a char named letter. When I print the value of letter, I get a surprise: a a is not the first letter of "banana". Unless you are a computer scientist. For technical reasons, computer scientists start counting from zero. The 0th letter (“zeroeth”) of "banana" is b. The 1th letter (“oneth”) is a and the 2th (“twooth”) letter is n. If you want the zereoth letter of a string, you have to pass 0 as an argument: char letter = fruit.charAt(0); 8.2 LengthThe next String method we’ll look at is length, which returns the number of characters in the string. For example: int length = fruit.length(); length takes no arguments and returns an integer, in this case 6. Notice that it is legal to have a variable with the same name as a method (although it can be confusing for human readers). To find the last letter of a string, you might be tempted to try something like int length = fruit.length();
char last = fruit.charAt(length); // WRONG!! That won’t work. The reason is that there is no 6th letter in "banana". Since we started counting at 0, the 6 letters are numbered from 0 to 5. To get the last character, you have to subtract 1 from length. int length = fruit.length();
char last = fruit.charAt(length-1); 8.3 TraversalA common thing to do with a string is start at the beginning, select each character in turn, do some computation with it, and continue until the end. This pattern of processing is called a traversal. A natural way to encode a traversal is with a while statement: int index = 0;
while (index < fruit.length()) {
char letter = fruit.charAt(index);
System.out.println(letter);
index = index + 1;
} This loop traverses the string and prints each letter on a line by itself. Notice that the condition is index < fruit.length(), which means that when index is equal to the length of the string, the condition is false and the body of the loop is not executed. The last character we access is the one with the index fruit.length()-1. The name of the loop variable is index. An index is a variable or value used to specify a member of an ordered set, in this case the string of characters. The index indicates (hence the name) which one you want. 8.4 Run-time errorsWay back in Section 1.3.2 I talked about run-time errors, which are errors that don’t appear until a program has started running. In Java run-time errors are called exceptions. You probably haven’t seen many run-time errors, because we haven’t been doing many things that can cause one. Well, now we are. If you use the charAt method and provide an index that is negative or greater than length-1, it throws an exception. You can think of “throwing” an exception like throwing a tantrum. When that happens, Java prints an error message with the type of exception and a stack trace, which shows the methods that were running when the exception occurred. Here is an example: public class BadString {
public static void main(String[] args) {
processWord("banana");
}
public static void processWord(String s) {
char c = getLastLetter(s);
System.out.println(c);
}
public static char getLastLetter(String s) {
int index = s.length(); // WRONG!
char c = s.charAt(index);
return c;
}
} Notice the error in getLastLetter: the index of the last character should be s.length()-1. Here’s what you get: Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: 6 at java.lang.String.charAt(String.java:694) at BadString.getLastLetter(BadString.java:24) at BadString.processWord(BadString.java:18) at BadString.main(BadString.java:14) Then the program ends. The stack trace can be hard to read, but it contains a lot of information. 8.5 Reading documentationIf you go to http://download.oracle.com/javase/6/docs/api/java/lang/String.html. and click on charAt, you get the following documentation (or something like it): public char charAt(int index) Returns the char value at the specified index. An index ranges from 0 to length() - 1. The first char value of the sequence is at index 0, the next at index 1, and so on, as for array indexing. Parameters: index - the index of the char value. Returns: the char value at the specified index of this string. The first char value is at index 0. Throws: IndexOutOfBoundsException - if the index argument is negative or not less than the length of this string. The first line is the method’s prototype, which specifies the name of the method, the type of the parameters, and the return type. The next line describes what the method does. The following lines explain the parameters and return values. In this case the explanations are redundant, but the documentation is supposed to fit a standard format. The last line describes the exceptions this method might throw. It might take some time to get comfortable with this kind of documentation, but it is worth the effort. 8.6 The indexOf methodindexOf is the inverse of charAt: charAt takes an index and returns the character at that index; indexOf takes a character and finds the index where that character appears. charAt fails if the index is out of range, and throws an exception. indexOf fails if the character does not appear in the string, and returns the value -1. String fruit = "banana";
int index = fruit.indexOf('a'); This finds the index of the letter ’a’ in the string. In this case, the letter appears three times, so it is not obvious what indexOf should do. According to the documentation, it returns the index of the first appearance. To find subsequent appearances, there is another version of indexOf. It takes a second argument that indicates where in the string to start looking. For an explanation of this kind of overloading, see Section 6.4. If we invoke: int index = fruit.indexOf('a', 2); it starts at the twoeth letter (the first n) and finds the second a, which is at index 3. If the letter happens to appear at the starting index, the starting index is the answer. So int index = fruit.indexOf('a', 5); returns 5. 8.7 Looping and countingThe following program counts the number of times the letter ’a’ appears in a string: String fruit = "banana";
int length = fruit.length();
int count = 0;
int index = 0;
while (index < length) {
if (fruit.charAt(index) == 'a') {
count = count + 1;
}
index = index + 1;
}
System.out.println(count); This program demonstrates a common idiom, called a counter. The variable count is initialized to zero and then incremented each time we find an ’a’. To increment is to increase by one; it is the opposite of decrement. When we exit the loop, count contains the result: the total number of a’s. 8.8 Increment and decrement operatorsIncrementing and decrementing are such common operations that Java provides special operators for them. The ++ operator adds one to the current value of an int or char. -- subtracts one. Neither operator works on doubles, booleans or Strings. Technically, it is legal to increment a variable and use it in an expression at the same time. For example, you might see something like: System.out.println(i++); Looking at this, it is not clear whether the increment will take effect before or after the value is printed. Because expressions like this tend to be confusing, I discourage you from using them. In fact, to discourage you even more, I’m not going to tell you what the result is. If you really want to know, you can try it. Using the increment operators, we can rewrite the letter-counter: int index = 0;
while (index < length) {
if (fruit.charAt(index) == 'a') {
count++;
}
index++;
} It is a common error to write something like index = index++; // WRONG!! Unfortunately, this is syntactically legal, so the compiler will not warn you. The effect of this statement is to leave the value of index unchanged. This is often a difficult bug to track down. Remember, you can write index = index+1, or you can write index++, but you shouldn’t mix them. 8.9 Strings are immutableAs you read the documentation of the String methods, you might notice toUpperCase and toLowerCase. These methods are often a source of confusion, because it sounds like they have the effect of changing (or mutating) an existing string. Actually, neither these methods nor any others can change a string, because strings are immutable. When you invoke toUpperCase on a String, you get a new String as a return value. For example: String name = "Alan Turing";
String upperName = name.toUpperCase(); After the second line is executed, upperName contains the value "ALAN TURING", but name still contains "Alan Turing". 8.10 Strings are incomparableIt is often necessary to compare strings to see if they are the same, or to see which comes first in alphabetical order. It would be nice if we could use the comparison operators, like == and >, but we can’t. To compare Strings, we have to use the equals and compareTo methods. For example: String name1 = "Alan Turing";
String name2 = "Ada Lovelace";
if (name1.equals (name2)) {
System.out.println("The names are the same.");
}
int flag = name1.compareTo (name2);
if (flag == 0) {
System.out.println("The names are the same.");
} else if (flag < 0) {
System.out.println("name1 comes before name2.");
} else if (flag > 0) {
System.out.println("name2 comes before name1.");
} The syntax here is a little weird. To compare two Strings, you have to invoke a method on one of them and pass the other as an argument. The return value from equals is straightforward enough; true if the strings contain the same characters, and false otherwise. The return value from compareTo is a weird, too. It is the difference between the first characters in the strings that differ. If the strings are equal, it is 0. If the first string (the one on which the method is invoked) comes first in the alphabet, the difference is negative. Otherwise, the difference is positive. In this case the return value is positive 8, because the second letter of “Ada” comes before the second letter of “Alan” by 8 letters. Just for completeness, I should admit that it is legal, but very seldom correct, to use the == operator with Strings. I explain why in Section 13.4; for now, don’t do it. 8.11 Glossary
8.12 ExercisesExercise 1
Write a method that takes a String
as an argument and that prints the letters backwards all on
one line.
Exercise 2
Read the stack trace in Section 8.4
and answer these questions:
Exercise 3 Encapsulate the code in Section 8.7 in a method named countLetters, and generalize it so that it accepts the string and the letter as arguments. Then rewrite the method so that it uses indexOf to locate the a’s, rather than checking the characters one by one. Exercise 4
The purpose of this exercise is to review encapsulation
and generalization.
String s = "((3 + 7) * 2)";
int len = s.length();
int i = 0;
int count = 0;
while (i < len) {
char c = s.charAt(i);
if (c == '(') {
count = count + 1;
} else if (c == ')') {
count = count - 1;
}
i = i + 1;
}
System.out.println(count); Exercise 5 The point of this exercise is to explore Java types and fill in some of the details that aren’t covered in the chapter.
Exercise 6 What is the output of this program? Describe in a sentence what mystery does (not how it works). public class Mystery {
public static String mystery(String s) {
int i = s.length() - 1;
String total = "";
while (i >= 0 ) {
char ch = s.charAt(i);
System.out.println(i + " " + ch);
total = total + ch;
i--;
}
return total;
}
public static void main(String[] args) {
System.out.println(mystery("Allen"));
}
} Exercise 7
A friend of yours shows you the following method and
explains that if number is any two-digit number, the program
will output the number backwards. He claims that if number is
17, the method will output 71. Is he right? If not, explain what the program actually does and modify it so that it does the right thing. int number = 17;
int lastDigit = number%10;
int firstDigit = number/10;
System.out.println(lastDigit + firstDigit); Exercise 8
What is the output of the following program? public class Enigma {
public static void enigma(int x) {
if (x == 0) {
return;
} else {
enigma(x/2);
}
System.out.print(x%2);
}
public static void main(String[] args) {
enigma(5);
System.out.println("");
}
} Explain in 4-5 words what the method enigma really does. Exercise 9
Exercise 10
A word is said to be “abecedarian” if the letters in the word appear in alphabetical order. For example, the following are all 6-letter English abecedarian words. abdest, acknow, acorsy, adempt, adipsy, agnosy, befist, behint, beknow, bijoux, biopsy, cestuy, chintz, deflux, dehors, dehort, deinos, diluvy, dimpsy
Exercise 11
A dupledrome is a word that contains only double letters,
like “llaammaa” or “ssaabb”. I conjecture that there
are no dupledromes in common English use. To test that
conjecture, I would like a program that reads
words from the dictionary one at a time and checks them for
dupledromity. Write a method called isDupledrome that takes a String and returns a boolean indicating whether the word is a dupledrome. Exercise 12
Exercise 13 If you did the GridWorld exercises in Chapter 5, you might enjoy this exercise. The goal is to use trigonometry to get the Bugs to chase each other. Make a copy of BugRunner.java named ChaseRunner.java and import it into your development environment. Before you change anything, check that you can compile and run it.
|
Like this book?
Are you using one of our books in a class?We'd like to know about it. Please consider filling out this short survey.
|