Computers are often used to automate repetitive tasks, such as searching for text in documents. Repeating tasks without making errors is something that computers do well and people do poorly.
In this chapter, you’ll learn how to use while
and for
loops to add repetition to your code. We’ll also take a first look at String
methods and solve some interesting problems.
Using a while
statement, we can repeat the same code multiple times:
int
n
=
3
;
while
(
n
>
0
)
{
System
.
out
.
println
(
n
);
n
=
n
-
1
;
}
System
.
out
.
println
(
"Blastoff!"
);
3 2 1 Blastoff!
The flow of execution for a while
statement is shown here:
Evaluate the condition in parentheses, yielding true
or false
.
If the condition is false
, skip the following statements in braces.
If the condition is true
, execute the statements and go back to step 1.
This type of flow is called a loop, because the last step “loops back around” to the first. Figure 6-1 shows this idea using a flowchart.
while
loopThe body of the loop should change the value of one or more variables so that, eventually, the condition becomes false
and the loop terminates. Otherwise, the loop will repeat forever, which is called an infinite loop:
int
n
=
3
;
while
(
n
>
0
)
{
System
.
out
.
println
(
n
);
// n never changes
}
while
(
n
!=
1
)
{
System
.
out
.
println
(
n
);
if
(
n
%
2
==
0
)
{
// n is even
n
=
n
/
2
;
}
else
{
// n is odd
n
=
3
*
n
+
1
;
}
}
The hard question is whether this program terminates for all values of n
. So far, no one has been able to prove it or disprove it! For more information, see the “Collatz conjecture” entry on Wikipedia.
Here is another while
loop example; this one displays the numbers 1 to 5:
int
i
=
1
;
while
(
i
<=
5
)
{
System
.
out
.
println
(
i
);
i
++;
// add 1 to i
}
Assignments like i = i + 1
don’t often appear in loops, because Java provides a more concise way to add and subtract by one. Specifically, ++
is the increment operator; it has the same effect as i = i + 1
. And –
is the decrement operator; it has the same effect as i = i - 1
.
int
i
=
2
;
while
(
i
<=
8
)
{
System
.
out
.
(
i
+
", "
);
i
+=
2
;
// add 2 to i
}
System
.
out
.
println
(
"Who do we appreciate?"
);
2, 4, 6, 8, Who do we appreciate?
The loops we have written so far have three parts in common. They start by initializing a variable, they have a condition that depends on that variable, and they do something inside the loop to update that variable.
for
(
int
i
=
2
;
i
<=
8
;
i
+=
2
)
{
System
.
out
.
(
i
+
", "
);
}
System
.
out
.
println
(
"Who do we appreciate?"
);
The for
loop is often easier to read because it puts all the loop-related statements at the top of the loop. Doing so allows you to focus on the statements inside the loop body. Figure 6-2 illustrates for
loops with a flowchart.
for
loopThere is another difference between for
loops and while
loops: if you declare a variable in the initializer, it exists only inside the for
loop. For example:
for
(
int
n
=
3
;
n
>
0
;
n
--)
{
System
.
out
.
println
(
n
);
}
System
.
out
.
println
(
"n is now "
+
n
);
// compiler error
The last line tries to display n
(for no reason other than demonstration), but it won’t work. If you need to use a loop variable outside the loop, you have to declare it outside the loop, like this:
int
n
;
for
(
n
=
3
;
n
>
0
;
n
--)
{
System
.
out
.
println
(
n
);
}
System
.
out
.
println
(
"n is now "
+
n
);
Notice that the for
statement does not say int n = 3
. Rather, it simply initializes the existing variable n
.
Like conditional statements, loops can be nested one inside the other. Nested loops allow you to iterate over two variables. For example, we can generate a “multiplication table” like this:
for
(
int
x
=
1
;
x
<=
10
;
x
++)
{
for
(
int
y
=
1
;
y
<=
10
;
y
++)
{
System
.
out
.
printf
(
"%4d"
,
x
*
y
);
}
System
.
out
.
println
();
}
Variables like x
and y
are called loop variables, because they control the execution of a loop. In this example, the first loop (for x
) is known as the outer loop, and the second loop (for y
) is known as the inner loop.
1 2 3 4 5 6 7 8 9 10 2 4 6 8 10 12 14 16 18 20 3 6 9 12 15 18 21 24 27 30 4 8 12 16 20 24 28 32 36 40 5 10 15 20 25 30 35 40 45 50 6 12 18 24 30 36 42 48 54 60 7 14 21 28 35 42 49 56 63 70 8 16 24 32 40 48 56 64 72 80 9 18 27 36 45 54 63 72 81 90 10 20 30 40 50 60 70 80 90 100
Strings provide a method named charAt
. It returns a char
, a data type that stores an individual character (as opposed to strings of them):
String
fruit
=
"banana"
;
char
letter
=
fruit
.
charAt
(
0
);
b | a | n | a | n | a |
0 | 1 | 2 | 3 | 4 | 5 |
if
(
letter
==
'A'
)
{
System
.
out
.
println
(
"It's an A!"
);
}
Character literals, like 'A'
, appear in single quotes. Unlike string literals, which appear in double quotes, character literals can contain only a single character. Escape sequences, like '\t'
, are legal because they represent a single character.
System
.
out
.
(
"Roman alphabet: "
);
for
(
char
c
=
'A'
;
c
<=
'Z'
;
c
++)
{
System
.
out
.
(
c
);
}
System
.
out
.
println
();
ABCDEFGHIJKLMNOPQRSTUVWXYZ
Java uses Unicode to represent characters, so strings can store text in other alphabets like Cyrillic and Greek, and nonalphabetic languages like Chinese. You can read more about it at the Unicode website.
In Unicode, each character is represented by a code point, which you can think of as an integer. The code points for uppercase Greek letters run from 913 to 937, so we can display the Greek alphabet like this:
System
.
out
.
(
"Greek alphabet: "
);
for
(
int
i
=
913
;
i
<=
937
;
i
++)
{
System
.
out
.
((
char
)
i
);
}
System
.
out
.
println
();
This example uses a type cast to convert each integer (in the range) to the corresponding character. Try running the code and see what happens.
for
and while
loops have the same capabilities; any for
loop can be rewritten as a while
loop, and vice versa. For example, we could have printed letters of the alphabet by using a while
loop:
System
.
out
.
(
"Roman alphabet: "
);
char
c
=
'A'
;
while
(
c
<=
'Z'
)
{
System
.
out
.
(
c
);
c
++;
}
System
.
out
.
println
();
You might wonder when to use one or the other. It depends on whether you know how many times the loop will repeat.
A for
loop is definite, which means we know, at the beginning of the loop, how many times it will repeat. In the alphabet example, we know it will run 26 times. In that case, it’s better to use a for
loop, which puts all of the loop control code on one line.
A while
loop is indefinite, which means we don’t know how many times it will repeat. For example, when validating user input as in “Validating Input”, it’s impossible to know how many times the user will enter a wrong value. In this case, a while
loop is more appropriate:
System
.
out
.
(
"Enter a number: "
);
while
(!
in
.
hasNextDouble
())
{
String
word
=
in
.
next
();
System
.
err
.
println
(
word
+
" is not a number"
);
System
.
out
.
(
"Enter a number: "
);
}
double
number
=
in
.
nextDouble
();
It’s easier to read the Scanner
method calls when they’re not all on one line of code.
for
(
int
i
=
0
;
i
<
fruit
.
length
();
i
++)
{
char
letter
=
fruit
.
charAt
(
i
);
System
.
out
.
println
(
letter
);
}
Because length
is a method, you have to invoke it with parentheses (there are no arguments). When i
is equal to the length of the string, the condition becomes false
and the loop terminates.
To find the last letter of a string, you might be tempted to do something like this:
int
length
=
fruit
.
length
();
char
last
=
fruit
.
charAt
(
length
);
// wrong!
This code compiles and runs, but invoking the charAt
method throws a StringIndexOutOfBoundsException
. The problem is that there is no sixth letter in "banana"
. Since we started counting at 0, the six letters are indexed 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
);
// correct
public
static
String
reverse
(
String
s
)
{
String
r
=
""
;
for
(
int
i
=
s
.
length
()
-
1
;
i
>=
0
;
i
--)
{
r
+=
s
.
charAt
(
i
);
}
return
r
;
}
String
fruit
=
"banana"
;
int
index
=
fruit
.
indexOf
(
'a'
);
// returns 1
int
index
=
fruit
.
indexOf
(
'a'
,
2
);
// returns 3
To visualize how indexOf
and other String
methods work, it helps to draw a picture like Figure 6-3. The previous code starts at index 2 (the first 'n'
) and finds the next 'a'
, which is at index 3.
String
of six charactersIf the character happens to appear at the starting index, the starting index is the answer. So fruit.indexOf(’a’, 5)
returns 5
. If the character does not appear in the string, indexOf
returns -1
. Since indexes cannot be negative, this value indicates the character was not found.
You can also use indexOf
to search for an entire string, not just a single character. For example, the expression fruit.indexOf("nan")
returns 2
.
When comparing strings, it might be tempting to use the ==
and !=
operators. But that will almost never work. The following code compiles and runs, but it always prints Goodbye!
regardless what the user types.
System
.
out
.
(
"Play again? "
);
String
answer
=
in
.
nextLine
();
if
(
answer
==
"yes"
)
{
// wrong!
System
.
out
.
println
(
"Let's go!"
);
}
else
{
System
.
out
.
println
(
"Goodbye!"
);
}
The correct way to compare strings is with the equals
method, like this:
if
(
answer
.
equals
(
"yes"
))
{
System
.
out
.
println
(
"Let's go!"
);
}
If two strings differ, we can use compareTo
to see which comes first in alphabetical order:
String
name1
=
"Alan Turing"
;
String
name2
=
"Ada Lovelace"
;
int
diff
=
name1
.
compareTo
(
name2
);
if
(
diff
<
0
)
{
System
.
out
.
println
(
"name1 comes before name2."
);
}
else
if
(
diff
>
0
)
{
System
.
out
.
println
(
"name2 comes before name1."
);
}
else
{
System
.
out
.
println
(
"The names are the same."
);
}
In “Formatting Output”, we learned how to use System.out.printf
to display formatted output. Sometimes programs need to create strings that are formatted a certain way, but not display them immediately (or ever). For example, the following method returns a time string in 12-hour format:
public
static
String
timeString
(
int
hour
,
int
minute
)
{
String
ampm
;
if
(
hour
<
12
)
{
ampm
=
"AM"
;
if
(
hour
==
0
)
{
hour
=
12
;
// midnight
}
}
else
{
ampm
=
"PM"
;
hour
=
hour
-
12
;
}
return
String
.
format
(
"%02d:%02d %s"
,
hour
,
minute
,
ampm
);
}
A statement that executes a sequence of statements repeatedly.
A variable that is initialized, tested, and updated in order to control a loop.
An integer variable or value used to indicate a character in a string.
An international standard for representing characters in most of the world’s languages.
The string ""
, which contains no characters and has a length of zero.
Defining two or more methods with the same name but different parameters.
The code for this chapter is in the ch06 directory of ThinkJavaCode2. See “Using the Code Examples” for instructions on downloading the repository. Before you start the exercises, we recommend that you compile and run the examples.
If you have not already read “Tracing with a Debugger”, now might be a good time. It describes the DrJava debugger, which is a useful tool for visualizing the flow of execution through loops.
Consider the following methods (main
and loop
):
public
static
void
main
(
String
[]
args
)
{
loop
(
10
);
}
public
static
void
loop
(
int
n
)
{
int
i
=
n
;
while
(
i
>
1
)
{
System
.
out
.
println
(
i
);
if
(
i
%
2
==
0
)
{
i
=
i
/
2
;
}
else
{
i
=
i
+
1
;
}
}
}
Draw a table that shows the value of the variables i
and n
during the execution of loop
. The table should contain one column for each variable and one line for each iteration.
What is the output of this program?
Can you prove that this loop terminates for any positive value of n
?
Let’s say you are given a number, a, and you want to find its square root. One way to do that is to start with a rough guess about the answer, x0, and then improve the guess by using this formula:
For example, if we want to find the square root of 9, and we start with , then , which is closer. We can repeat the procedure, using x1 to calculate x2, and so on. In this case, and . So the repetition converges quickly on the correct answer.
Write a method called squareRoot
that takes a double
and returns an approximation of the square root of the parameter, using this technique. You should not use Math.sqrt
.
As your initial guess, you should use . Your method should iterate until it gets two consecutive estimates that differ by less than 0.0001. You can use Math.abs
to calculate the absolute value of the difference.
One way to evaluate is to use the infinite series expansion:
The ith term in this series is . Write a method named gauss
that takes x
and n
as arguments and returns the sum of the first n
terms of the series. You should not use factorial
or pow
.