Java Carter
Java Carter
to
Computer Science
Using Java
John Carter
An Introduction to Computer Science Using Java
5 4
ii
To Laryssa
Contents
Preface xi
1 Getting Started 1
1.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.2 A First Program . . . . . . . . . . . . . . . . . . . . . . . . 6
1.3 Developing Java Programs . . . . . . . . . . . . . . . . . . . 13
1.4 Integer Types . . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.5 Other Primitive Types . . . . . . . . . . . . . . . . . . . . . 19
1.6 Identifiers and Variables . . . . . . . . . . . . . . . . . . . . 22
1.7 Assigning Values to Variables . . . . . . . . . . . . . . . . . 26
1.8 String Variables . . . . . . . . . . . . . . . . . . . . . . . . . 31
1.9 Printing and Reading Values of Variables . . . . . . . . . . 35
1.10 Constants . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
1.11 Avoiding Errors and Debugging . . . . . . . . . . . . . . . . 43
1.12 Review Exercises 1 . . . . . . . . . . . . . . . . . . . . . . . 47
v
3 Decision Making 91
3.1 Decisions and Relational Expressions . . . . . . . . . . . . . 92
3.2 Comparing Strings . . . . . . . . . . . . . . . . . . . . . . . 96
3.3 The if Statement . . . . . . . . . . . . . . . . . . . . . . . 101
3.4 Boolean Operators . . . . . . . . . . . . . . . . . . . . . . . 106
3.5 Nested if Statements . . . . . . . . . . . . . . . . . . . . . 111
3.6 Choosing From Many Alternatives . . . . . . . . . . . . . . 116
3.7 Avoiding Errors and Debugging . . . . . . . . . . . . . . . . 119
3.8 Review Exercises 3 . . . . . . . . . . . . . . . . . . . . . . . 124
4 Repetition 133
4.1 while Statements . . . . . . . . . . . . . . . . . . . . . . . . 134
4.2 do Statements . . . . . . . . . . . . . . . . . . . . . . . . . . 139
4.3 Simple for Statements . . . . . . . . . . . . . . . . . . . . . 141
4.4 Variations on for Statements . . . . . . . . . . . . . . . . . 145
4.5 Comparing Loop Structures . . . . . . . . . . . . . . . . . . 148
4.6 Nesting Loop Structures . . . . . . . . . . . . . . . . . . . . 152
4.7 Avoiding Errors and Debugging . . . . . . . . . . . . . . . . 156
4.8 Review Exercises 4 . . . . . . . . . . . . . . . . . . . . . . . 160
5 Methods 169
5.1 Basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170
5.2 Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . 174
5.3 Methods that Return Values . . . . . . . . . . . . . . . . . . 179
5.4 Method Overloading . . . . . . . . . . . . . . . . . . . . . . 183
5.5 Methods that Return boolean Values . . . . . . . . . . . . 187
5.6 Scope and Accessibility . . . . . . . . . . . . . . . . . . . . 189
5.7 Programming with Methods . . . . . . . . . . . . . . . . . . 194
5.8 Avoiding Errors and Debugging . . . . . . . . . . . . . . . . 203
5.9 Review Exercises 5 . . . . . . . . . . . . . . . . . . . . . . . 206
vi
6.9 Avoiding Errors and Debugging . . . . . . . . . . . . . . . . 259
6.10 Review Exercises 6 . . . . . . . . . . . . . . . . . . . . . . . 262
8 Arrays 307
8.1 Tables and Arrays . . . . . . . . . . . . . . . . . . . . . . . 308
8.2 Using Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . 313
8.3 Multi-Dimensional Arrays . . . . . . . . . . . . . . . . . . . 317
8.4 Arrays of Objects . . . . . . . . . . . . . . . . . . . . . . . . 326
8.5 Partially Filled Arrays . . . . . . . . . . . . . . . . . . . . . 330
8.6 Avoiding Errors and Debugging . . . . . . . . . . . . . . . . 335
8.7 Review Exercises 8 . . . . . . . . . . . . . . . . . . . . . . . 338
9 Strings 347
9.1 String Objects . . . . . . . . . . . . . . . . . . . . . . . . . 348
9.2 String Methods . . . . . . . . . . . . . . . . . . . . . . . . . 351
9.3 Arrays of Strings . . . . . . . . . . . . . . . . . . . . . . . . 360
9.4 The StringTokenizer Class . . . . . . . . . . . . . . . . . 364
9.5 Avoiding Errors and Debugging . . . . . . . . . . . . . . . . 369
9.6 Review Exercises 9 . . . . . . . . . . . . . . . . . . . . . . . 373
vii
11 Recursion 423
11.1 Everyday Recursion . . . . . . . . . . . . . . . . . . . . . . 424
11.2 Recursion in Mathematics . . . . . . . . . . . . . . . . . . . 427
11.3 Recursive Queries . . . . . . . . . . . . . . . . . . . . . . . . 431
11.4 Recursive Commands . . . . . . . . . . . . . . . . . . . . . . 439
11.5 Recursion with Strings . . . . . . . . . . . . . . . . . . . . . 446
11.6 Backtracking Algorithms . . . . . . . . . . . . . . . . . . . . 450
11.7 Quicksort . . . . . . . . . . . . . . . . . . . . . . . . . . . . 455
11.8 Algorithm Analysis Revisited . . . . . . . . . . . . . . . . . 461
11.9 Avoiding Errors and Debugging . . . . . . . . . . . . . . . . 466
11.10Review Exercises 11 . . . . . . . . . . . . . . . . . . . . . . 468
viii
F The Javadoc Program 631
Index 672
ix
Preface
Many Exercises
There are exercises following every section in the text. These exercises
reinforce immediately the ideas in each section and enable the reader to
integrate each new idea into the previous concepts. The exercises follow-
ing each section have various forms including short, drill type questions,
questions that are similar in structure to the examples of that section, and
short programming exercises. In addition, at the end of each chapter, there
is a large set of review exercises with questions like those after each section
as well as larger problems, some of which might take a student many hours
to solve.
xi
Many Examples
The book contains as little textual material as possible. Most ideas are
given in short sections with many examples illustrating the new concepts.
Avoiding Errors and Debugging
Sections on these topics are included at the ends of chapters. These sections
point out the pitfalls that may occur in implementing the new ideas of the
chapters, suggest ways of avoiding these pitfalls, and examine techniques
for detecting and correcting errors that may arise. Having these sections
throughout the text should encourage students to develop these important
skills as they proceed.
A Variety of Applications
The text is not aimed strictly at mathematically oriented students. Al-
though Chapter 2 is devoted to performing calculations and using math-
ematical functions, numerical work is not emphasized elsewhere and no
mathematics beyond that done in high school is required to understand
the material in the later chapters.
Real Jobs — Real People
Students starting out in computer studies are often curious about where
they might end up if they were to continue in the field. To help answer
their questions, I have included, at the end of various chapters, short articles
outlining the jobs that some people have, how they arrived at those jobs,
and how they feel about them.
Answers and Solutions
Appendix G contains answers to all non-programming questions in the text.
In addition, a solutions manual is available only to instructors who adopt
the text. This manual contains complete solutions to all problems other
than the very large problems designated as Projects that appear at the
ends of chapters.
xii
contains an introduction to Java’s I/O, both with and without files. If you
want to use In, it is available on the web at
http://www.ecf.utoronto.ca/∼jcarter/text/In.java
Appendix A also contains a class called Out that gives a programmer more
control over the format of output than Java’s print or println methods.
This class is also available on the web at
http://www.ecf.utoronto.ca/∼jcarter/text/Out.java
Instructions for the use of both In and Out are given in Appendix A.
Topic Ordering
In ordering the topics of a text using Java, the central question that must
be answered is: where does one place objects? Although it is common to
start with objects and, possibly, graphical user interfaces, I have not done
so. It is my feeling that students initially find the concept of an object to
be a difficult one. Before getting into a full-scale study of objects, I think
that it is easier for students if they already have some familiarity with the
language. In addition, by delaying the introduction of creation of classes
for objects, by the time we do look at them, students have the tools to do
something interesting with their objects.
Although the text does not ask students to create their own types of
objects until Chapter 6, they are introduced to the use of objects of the
class String in Chapter 1. By learning how to create and compare strings,
students should get a feel for the use of objects as they are learning the fun-
damentals of programming. By the time that they reach the more difficult
concepts associated with objects, they should have enough familiarity with
both the tools of Java and the basics of object manipulation to proceed
fairly easily.
Graphical ideas are not dealt with until very close to the end of the
text (in Chapter 13 and Appendix D). Instructors who wish to incorporate
graphical ideas into a course can teach this material near the beginning of
the course. Those who do not want to deal with graphical ideas can avoid
them altogether.
xiii
or decrement operators (in Section 2.3), trigonometric, exponential, and
logarithmic methods (in Section 2.6), and the case statement (in Section
3.6) can all be omitted without fear of loss of continuity. After Chapter
6, the reader is fairly free to choose the order of topics, with the following
exceptions:
Acknowledgments
The development of this book has taken a long time and many people
have been helpful along the way. Guy Lemieux, Baochun Li, and James
MacLean all used preliminary versions of the text in their classes at the
University of Toronto and made a number of suggestions for improvements.
Gerry Heffernan at North Toronto Collegiate Institute also used prelimi-
nary versions of the text and provided many corrections and improvements.
xiv
I am also grateful to my students who put up with early, error-laden ver-
sions of the book (and corrected many of those errors). The names of many
of them are scattered throughout the text.
I would particularly like to thank Brian Auyeung and Humie Leung
who wrote the solutions that appear in the Instructor’s Solution Manual
and Jacqueline Carter who wrote many of the Real Jobs — Real People
biographies that appear at the ends of chapters throughout the book.
Finally, I would like to thank my family: Laryssa, for her constant
support and encouragement and Toby, for waiting patiently for walks that
were often delayed while I was stuck at my computer.
xv
Chapter 1
Getting Started
1.1 Introduction
Hardware
Although computers vary widely in cost, size, speed, and capabilities, they
share a number of organizational features. These are shown in the following
diagram:
Central
Processing
Unit
The control unit interprets the instructions of the programs and sends
commands to various other parts of the computer so that the instructions
are executed. When commanded by the control unit, the arithmetic-logic
unit performs arithmetic and makes very simple logical decisions such as
determining whether or not two numbers have the same value.
The memory of the computer is the part that stores both the instruc-
tions that the CPU is to perform and the data on which these instructions
are to be performed. We can think of the memory as an electronic chalk-
board in which the computer keeps notes on both what it is to do and the
values it is to use. Memory is organized into numbered storage locations,
each of which is capable of holding a small amount of information. The
number assigned to each storage location is called its address. The CPU
can examine the contents of these locations, move data from one location
to another, send data out of memory to a device such as a printer, and
bring new data into memory from a device such as a keyboard.
To communicate with the world, each computer must have some input
and output devices, collectively referred to as I/O devices. A small com-
puter may have only a couple of input devices — a keyboard and a mouse,
and one output device — a screen. Larger computer systems, however,
may have many other input devices such as cameras, digitizing pads, and
microphones along with many output devices such as printers and speakers.
Devices known as auxiliary storage units are found with virtually all
computer systems. These devices are sometimes classified as I/O devices,
and sometimes as an integral part of the computer system itself. They
include disk drives, tapes, and CD drives. They are used very much like
memory, with some important differences.
1. They are capable of holding a much larger volume of data.
2. Accessing information in them takes far longer than accessing data
held in memory.
3. The information stored in these units is retained in them from one
session on the computer to another. This is not generally true of
memory; each time that a computer is turned off, the contents of
memory are usually lost.
4. The information in auxiliary storage is usually organized in the form
of files. A file can contain a set of instructions or a collection of data.
The way in which the files are organized varies from one computer
system to another and even from one storage unit to another. The
local system documentation will have to be consulted for details.
4 CHAPTER 1. GETTING STARTED
Software
As we have already noted, every computer system must have software
as well as hardware before it can do any computing. Each computer’s CPU
uses instructions encoded electronically in memory. This code is called, rea-
sonably enough, machine code. Although it is possible to write programs
directly in machine code, this process has a number of disadvantages. It
is very easy for a programmer to make a mistake when writing these pro-
grams, they are very difficult to follow, and, because different machines
use different machine codes, a program written in the machine code of one
computer will have to be completely rewritten in order to work on some
other computer.
For these reasons, most programming is now done using high-level
languages. Programs in these languages are reasonably easy to write, un-
derstand, and modify. There are many such high-level languages in use
today; Java is such a language.
A program written in a high-level language cannot be performed di-
rectly by the hardware of a computer. Instead, another program, usually
called a compiler, must be used to translate the high-level language into
machine code. The original program in the high-level language is called the
source program while the corresponding machine code is called the object
program. Thus, to have a computer perform the instructions of a program
written in a high-level language, we must go through two stages:
1. Compilation: Here the compiler is brought into memory and its
instructions are performed. The instructions of the compiler cause
the computer to read the source program and translate it into the
corresponding object program.
2. Execution: The machine code instructions of the object program
are performed.
With Java, the process is slightly more complicated. At the compila-
tion stage, a Java source program is not compiled into the machine code of
a particular machine. Instead, all source programs are compiled into the
same machine language — for a machine that does not exist ! This compiled
code is called Java byte code. In order to run a compiled Java program
on a real machine, another program, called an interpreter, can be used to
translate from byte code to the machine code for the actual machine on
which the program is being run. This program is called the Java Virtual
Machine (JVM). Since different computer systems have different machine
1.1. INTRODUCTION 5
languages, each computer system on which we want to run Java must have
its own JVM. Luckily, this is almost always the case; JVM interpreters
have been written for a wide range of computer systems.
The advantage of this added complexity is that the writer of a Java
program need not be concerned about the machine on which the program
will be run. For example, if a Java program is written on an Apple computer
and compiled into byte code, it can then be sent out over a network and
run on any other machine connected to the network. It is this feature that
has led the creators of the language at Sun Microsystems to characterize
Java as a language in which we “write once, run anywhere”.
Exercises 1.1
1. Name the principal parts of
We begin our study of Java with a program that prints a message greeting
the world.
Example 1
class Greet
{
// This program prints a simple message
public static void main (String[] args)
{
System.out.println("Hello, world");
}
}
1. class
All Java programs are contained in a class. The start of a class
is indicated by the reserved word class. Java contains about fifty
reserved words, all of which are listed later in this chapter.
2. Greet
This is the name that identifies the class. The choice of a name is,
within limits, up to the programmer but, customarily, classes have
names that start with an upper case letter. We chose the name Greet
because the program greets the world but we could have called it
FirstProgram if we had chosen to do so. In fact, as far as the
computer is concerned, we could have called the class Athena or
Saskatoon but it is better, for the benefit of your human readers,
to use a meaningful name.
3. { }
The beginning and end of any section of a Java program are indicated
1.2. A FIRST PROGRAM 7
by brace brackets, { and }. The beginning and end of the class are
indicated by the { in the second line and the } in the last line of the
program.
6. { }
The inner pair of brace brackets defines the beginning and end of the
definition of the main method, just as the outer pair of brace brackets
defines the limits of the definition of the class.
7. System.out.println("Hello, world");
This statement calls on a method called println to send the string
of characters Hello world contained in double quotes to some out-
put device (probably your computer’s screen). Whatever the output
device is, we can think of it as a page of paper. The println method,
given a string of characters, writes the string on the page and then
moves on to the next line of the page, ready to print the next item, if
there is one. As we suggested in discussing the header of the method,
1 Such programs are sometimes called application programs. As we will see later when
we study applets, Java has other types of programs that do not have a main method.
8 CHAPTER 1. GETTING STARTED
the details of the use of println will be made clear later. For now,
just use it exactly as shown, with an upper case S on System, dots be-
tween System, out, and println, and including both the parentheses
and the terminating semi-colon.
You should notice in Example 1 the way in which the parts of the
program have been indented. Everything within the brace brackets that
enclose the body of the class is indented two spaces. Similarly, everything
within the brace brackets that enclose the body of the main method is
indented two more spaces past the header of the method.
Indentation is not necessary for the computer but it can improve the
readability for a human. Since your programs should be clear as well as
correct, you should always indent them. Your instructor might suggest an
indentation style that is slightly different from the one that we use. The
important thing is to use a style that is both clear and consistent.
Example 2
As far as a computer is concerned, the program of the previous example is
exactly equivalent to either of the following:
class Greet{public static void main(String[]
args){System.out.println("Hello, world");}}
or
class Greet {
public
static
void
main( String [
] args )
{
System.out.println
( "Hello, world"
) ;
}
}
1.2. A FIRST PROGRAM 9
3. Extra blanks, even entire blank lines, do not usually change the mean-
ing of the surrounding symbols. Blanks can even be inserted before
or after dots so that System . out . println would be correct.
(It is, however, considered bad style to have blanks around dots; you
should avoid it.) Finally, extra blanks inside a string (inside double
quotes) will not be ignored so that the output produced by printing
the strings "Hello , world" and "Hello,world" would be different.
Example 3
The following program illustrates some uses of print and println.
class PrintDemo
{
public static void main (String[] args)
{
System.out.println("Never put off till tomorrow");
System.out.println();
System.out.print("what you can do");
System.out.println(" the day");
System.out.println();
System.out.println("after" + " " + "tomorrow.");
}
}
This will produce the following output.
Never put off till tomorrow
after tomorrow.
Example 4
The statement
System.out.println("Angela said, "
+ "\"That’s ridiculous, Bram.\"");
would print
Angela said, "That’s ridiculous, Bram."
1.2. A FIRST PROGRAM 11
Example 5
The statement
System.out.println("A backslash: \\\na double quote: \"");
would produce the output
A backslash: \
a double quote: "
Exercises 1.2
1. What does this program print?
class Advice
{
public static void main (String[] args)
{
System.out.print("If at first ");
System.out.println("you don’t succeed" + ",");
System.out.print("failure may be ");
System.out.println("your style");
}
}
3. Write a single Java statement that would print the following, exactly
as shown.
A slash is "/"
while
a backslash is "\"
4. The following program contains a number of errors. Rewrite the
program with the errors corrected.
class BadForm
public void main (string() args);
{
System.Out.Println(’What’s wrong with this?’)
}
6. Write Java programs to print each design. You might find it helpful
to use squared paper in planning the appearance of your output.
PARALLELOG APEZO D
A R R I I I
R A TRAPEZOID A A
ALLELOGRAM M M
O O
N N
D
There are a number of ways to create and run a Java program. If you are
working with Java on your own, you should feel free to choose the one that
suits you. If, as is more likely, you are in a computer science course, your
instructor may specify the way that the local system operates. Assuming
that you are free to choose a program development environment, here is a
brief outline of the choices available.
Almost everything that you will need to develop Java programs is
available for free from Sun Microsystems, the developers of Java. To ob-
tain a copy of Java from Sun, go to their web site at http://java.sun.com
and download the Java 2 Standard Edition (J2SE) Software Development
Kit (SDK) appropriate for your computer’s operating system. The SDK
contains a variety of tools, including a compiler, an interpreter, and the
Application Programming Interface (API) — a huge library of classes con-
taining methods that you will be using in your programs. You may want
the latest version of the SDK but, if your institution is using an earlier
one, it is probably best to get that (to be sure that programs developed at
home will also run when marked at school). To use the SDK directly, we
must operate in a text-based environmnent, without using a mouse. We
will examine the details of this process shortly.
As an alternative, there are many versions of programs called Inte-
grated Development Environments (IDE’s) that provide, as the name sug-
gests, an environment that is designed to assist the user in developing Java
programs. An IDE can help you in organizing files, writing programs, find-
ing mistakes in programs, and running programs — using both a mouse
and the keyboard. Some IDE’s come complete with all tools necessary for
program development while others require that the SDK be obtained from
Sun. Some are free; others are quite expensive. If you are interested in
using an IDE, a search engine on the web pointing to “Java IDE” will get
you a great deal of information about what is currently available.
To start the development of a program, you must first write the source
program using some text editor. If you are using an IDE, then this will be
part of the IDE. If not, then you can use one of the text editors available
with your operating system. As examples, Windows has a text editor called
Notepad; UNIX systems have a number of editors, including pico, vi, and
14 CHAPTER 1. GETTING STARTED
emacs; and X-Windows has nedit and xedit. It is better to use a text
editor rather than a word processor since we want to create a simple text
file while word processors tend to produce files that include information
about layout, fonts, and so on. The program must be saved in a file with
a name of the form2
<name>.java
where <name> is exactly the same name that was used for the class. The
.java part is called an extension. As an example, the source file for a
program contained in a class called Sample should be called Sample.java
(with the case of the letters matching exactly).
Once the program has been written and saved, it must then be com-
piled into Java byte code. If you are working with an IDE, then a mouse
click should invoke the Java compiler. If you are working in a command
window, the compiler can be invoked for the program in the file called
Sample.java by writing
javac Sample.java
Before converting the program to byte code form, the compiler first
tries to find mistakes in the program. It cannot find all mistakes but it
is quite clever and it can detect many of them. If errors are detected
during compilation, a message will be printed and compilation will not
be completed. If this occurs, the programmer must analyze the problem,
correct the source code, and repeat the attempt to compile the program.
With a complex program, this process may be repeated many times before
a program compiles without errors.
Once the compiler is happy with the form of the program, it will
compile it to produce a byte code file called <name>.class. At this point,
we must call upon the Java Virtual Machine (JVM) to run the program
by interpreting the byte code and executing the program’s instructions. In
an IDE, another mouse click should allow you to do this. In a command
window, the JVM can be invoked by writing
java <name>
As an example, for the program originally created in the file Sample.java
and compiled into the file Sample.class, we would run the program by
writing
2 Throughout this book, items printed in a font like this and surrounded by angle
brackets are used to indicate forms. Actual values are obtained by substituting some-
thing of the required form, without the angle brackets. Anything not contained in angle
brackets should appear exactly as shown.
1.3. DEVELOPING JAVA PROGRAMS 15
java Sample
Notice that there is no extension on the file name in this command.
?
Attempt
Compilation
?
H
HH No Analyze
H - Compilation
H
H Correct?
H Errors
H
Yes
?
Attempt
Execution
? H Analyze
H
H
H No - Execution
H Correct?
H
Errors
HH
Yes
?
Done
16 CHAPTER 1. GETTING STARTED
Exercises 1.3
1. Copy and run the program shown in Example 1 on page 6.
The int type is adequate for most tasks and you should use it for almost
all situations that require an integer. The types byte or short can be used
if we need to save space in memory while long can be used if we require
integers with an extended range.
Example 1
The integer value 2 000 000 could be represented by an int (maximum
value 231 − 1 = 2 147 483 647) but not by a short (maximum value
215 − 1 = 32 767).
Example 2
The following are all valid integer constants. The first five would be stored
as int values while the last one would be stored as a long value.
4 0 113 −357462 +23 9876543210L
3 InJava, numbers with leading zeros are not considered to be standard, base 10
numerals. For a detailed explanation of this somewhat bizarre rule, see Appendix C.
18 CHAPTER 1. GETTING STARTED
Integer constants must not be written with a decimal point and they
cannot contain any separators between digits.
Example 3
Each of the following is an illegal integer constant.
(a) 37.0 contains a decimal point
(b) −12 562 contains a blank between digits
(c) 1,233,985 contains commas between digits
Exercises 1.4
1. A group of 4 bits is called a nibble. How many different values could
be represented by a nibble?
2. What is the value of the largest positive integer that can be repre-
sented by each of the following types?
(a) byte (b) long
(c) short (d) int
4. State, with reasons, which of the following are not legal Java integer
constants.
(a) -47 (b) 23.
(c) -0 (d) 22 900
Example 1
Each of the following are valid floating point constants.
5.23 .3 2818. -0.0002 6.7f
The first four would be stored as double values while the last one would
be stored as a float value.
Example 2
(a) 5.6e2 represents the value 5.6 × 102 = 560
(b) 37E-4 represents the value 37 × 10−4 = 0.0037
(c) -0.667e-2 represents the value −0.667 × 10−2 = −0.00667
world’s languages along with a wide variety of symbols and shapes can be
represented by a character in Unicode. You need not be concerned about
the details of the Unicode encoding scheme but, if you are interested, look
at http://unicode.org/ .
Characters in Java programs are of type char. We show constants
representing single characters by enclosing them in apostrophes (also known
as single quotes).
Example 3
The following are all valid char constants.
’A’ ’$’ ’+’ ’7’
Exercises 1.5
1. Rewrite in standard decimal form.
(a) 2.94e1 (b) 0.0004E3
22 CHAPTER 1. GETTING STARTED
Cyrillic and other alphabets. Of course, your system might not be able to print such
letters but they are valid in Java.
1.6. IDENTIFIERS AND VARIABLES 23
Example 1
From the following list, select (with reasons) those names that should not
be used as identifiers of Java variables.
firstChoice this second-time
3rdTime monthly_total Last
valueIn$ x27 word Length
24 CHAPTER 1. GETTING STARTED
Example 2
To request that space in memory be allocated to an int variable to be
called age, we could write
int age;
We can illustrate the result of this declaration as follows:
age
The box represents the space in memory allocated to the variable age.
Notice that, at this point, there is nothing in the box. This is because, at
this point, the variable age does not yet have a value.
1.6. IDENTIFIERS AND VARIABLES 25
Example 3
The declaration
double length, width, height;
creates three variables: length, width, and height, all of type double.
The result of this declaration is shown in the following diagram.
Exercises 1.6
1. What is the difference between identifiers used for classes and those
used for variables and methods?
2. Identify, with reasons, any identifiers that should not be used for Java
variables.
(a) digitSum (b) switch
(c) retail price (d) heightPlusDepth
(e) value@Start (f) priceIn£
26 CHAPTER 1. GETTING STARTED
Example 1
The statements
int i = 10;
double x = 2.8, y = 0.1;
will declare the variables i, x, and y and give them values of 10, 2.8, and
0.1 respectively. The results of executing these statements are illustrated
in the next diagram.
i x y
10 2.8 0.1
<identifier> = <value>;
From To
byte short, int, long, float, double
short int, long, float, double
char int, long, float, double
int long, float, double
long float, double
float double
Conversions other than the ones listed above can result in the loss
of information. Such conversions are called narrowing conversions and
should be treated with great care. Because data can be lost in narrowing
conversions, the Java compiler complains if you attempt to perform such
a conversion, even if the value in the wider representation can safely be
stored in the narrower representation.
If you are certain that a narrowing conversion is safe, you can force
Java to make the conversion by performing a cast from one type to another.
To perform a cast, write the name of the desired type, in parentheses, in
front of the value to be converted.
Example 2
The following fragment is valid. It will cause the value 2.5 to be stored in
the float variable f.
float f;
f = (float) 2.5;
Example 3
The following statement is valid. It will cause the integer value 75 to be
stored in the short variable s.
short s = 75;
Here no cast is necessary because the constant integer value being assigned
to the short variable is within the permissible range for short values and
1.7. ASSIGNING VALUES TO VARIABLES 29
the compiler can see that the assignment is valid. If we were to write
short s = 75000;
the compiler would notice that 75 000 is outside the range of a short
variable and it would produce an error message.
A cast is a kind of promise to the compiler that you know what you are
doing. If you lie to the compiler, it will trust you and compile the program,
but the result may not be correct.
Example 4
The fragment
Example 5
The following fragment will store the value 87 in the int variable i and
-32 in j.
Example 6
The following fragment will assign the value 1234 to the int variable i
and the value 2.71828 to the double variable d. Note that a cast is used
to convert the long to an int but no cast is necessary for the widening
conversion from float to double.
long l = 1234L;
float f = 2.71828F;
int i = (int) l;
double d = f;
Exercises 1.7
1. Suppose that the following declarations have been made:
int number;
double mass;
float density;
char symbol;
byte age;
long limit;
You may have heard that Java is an object oriented language. Although
there are only eight different primitive types, the number of object types
is unlimited. Many are predefined but we can also create our own types
of objects, to serve our own purposes. We will be studying objects very
extensively later on but, for now, we will only be using one of the most
common and useful types of objects available in Java — the string. Strings
in Java are objects of type String (written exactly as shown, with an upper
case S). We have already encountered string constants but we can also have
string variables. To declare a variable of type String, we proceed as we
would for any primitive type.
32 CHAPTER 1. GETTING STARTED
Example 1
The declaration
String s;
will create a variable s of type String whose value is undefined, as shown
in the next diagram.
s
Example 2
If we have declared s to be of type String, we can assign a value to s by
writing
s = "Sample";
Here Java creates a new String object containing the string "Sample" but
this is not stored in s. Instead, the value in s becomes a reference to the
location in memory that contains the string. The result is illustrated in
the next diagram where the arrow indicates that s contains a reference to
the location occupied by the string.
- "Sample"
Before going on, you should be sure that you understand what is hap-
pening here. A variable of type String, like s in our examples, does not
1.8. STRING VARIABLES 33
Example 3
The fragment
String s = "Joanne";
s = "Jacqueline";
first sets the variable s to refer to a string object containing "Joanne"
and then sets s to refer to a new string object containing "Jacqueline"
(at which point the object containing "Joanne" is lost). The result is
illustrated in the next diagram.
- "Jacqueline" "Joanne"
Example 4
Suppose that we have created a string object by writing
String s = "a string";
to produce the following situation:
- "a string"
If we now write
String t = s;
the value in s will be copied into t. Since s and t now contain the same
value and they are both reference variables, they both refer to the same
string.
s t
- "a string"
The print and println methods that we have been using to print
string constants can also be used to print string variables. We can also use
the concatenation operator to print any combination of string values.
Example 5
If the String variable answer has the value "Everest", then the statement
System.out.println("The highest peak is " + answer + ".");
would print
The highest peak is Everest.
1.9. PRINTING AND READING VALUES OF VARIABLES 35
Exercises 1.8
1. Draw diagrams like those shown in the text to illustrate the result of
executing the following statements.
(a) int a = 1; (b) String a = "first";
String b = "2"; String b = "second";
String c = "cat";
String d = "dog";
String s = c;
c = d;
d = s;
System.out.println("c is " + c);
System.out.println("d is " + d);
The print and println methods that we have used to print strings can
also be used to print values of variables of any type.
Example 1
The following program prints the value of an int variable.
class PrintValue
{
// This program assigns a value to the variable
// "number" and then prints that value.
36 CHAPTER 1. GETTING STARTED
Example 2
The following program will produce an error message if we attempt to
compile it.
class NoValue
{
// This program is wrong because the variable
// "number" is never given a value.
public static void main (String[] args)
{
int number;
System.out.println(number);
}
}
Example 3
If the String variable friend refers to the string "Nadine" and the int
variable age has the value 17, then the statement
System.out.println(friend + " is " + age + " years old");
will print
Nadine is 17 years old
We must be careful when we print more than one variable with one
print or println statement as the results may not be what we want.
Example 4
If the byte variables height and width have values 4 and 6, then the
statement
System.out.println("Dimensions are " + height + width);
will print
Dimensions are 46
Example 5
Consider the following program.
class PrintValues
{
public static void main (String[] args)
{
boolean b = true;
float f = 12.3456789f;
double d1 = 1234.567898765432;
double d2 = 1234.5678;
System.out.println(b);
System.out.println(f);
System.out.println(d1);
System.out.println(d2);
}
}
It should produce the following output
true
12.345679
1234.567898765432
1234.5678
Example 6
To read a value into the int variable i, we can use the method getInt that
reads a single integer from the standard input device, usually the keyboard.
The form of the statement that reads the value is:
i = In.getInt();
Notice the use of parentheses with the getInt method. Any time that
we use a method in Java, we must include parentheses, even if, as is the
case here, the method has no argument.5
Other methods in the class In include: getLong to read a long value,
getChar to read a single character, getString to read a string, getFloat
to read a float value, and getDouble to read a double value. They are
all used in the same way.
The methods all assume that each input value is terminated by a
newline. Thus, to use them at a keyboard, you must press the <enter> key
after keying in the value. If incorrect input is entered, the methods that
were expecting numeric input give a value of zero. If the user simply hits
the <enter> key when entering a string, the method getString produces
the value "", the empty string that contains no characters.
Now that we have methods for both reading and printing, we can write
interactive programs, ones that ask or prompt the user for input, wait until
the user responds to the prompt by providing some input, process this
input in some way, and produce some output.
Example 7
This program illustrates a simple interactive process.
class Interactive
{
public static void main (String[] args)
{
// obtain name and age
System.out.println("What is your first initial?");
char firstInitial = In.getChar();
5 We have already seen this in the use of System.out.println(); to print a blank
line.
40 CHAPTER 1. GETTING STARTED
// respond to input
System.out.println("Well, " + firstInitial + ". "
+ familyName + " - "
+ "I see that your age is " + age);
}
}
N
Fung
17
Exercises 1.9
1. What would be printed by each fragment?
1.10 Constants
Example 1
The statements that follow associate the identifier CLASS_SIZE with the
int value 30 and TERMINATOR with the char value ’*’.
final int CLASS_SIZE = 30;
final char TERMINATOR = ’*’;
Notice that the identifiers used here are made up of upper case letters
with, possibly, the underscore for clarification. This is not required by Java
but it is a common convention, one that you should follow.
Once an identifier has been declared to be final, its value can never
be changed at any point in the program. An attempt to do so will produce
an error message. It can, however, be used anywhere that a literal value of
that type could be used.
42 CHAPTER 1. GETTING STARTED
6 The values are not exact, but they are correct to 16 significant digits.
1.11. AVOIDING ERRORS AND DEBUGGING 43
Exercises 1.10
1. The text suggests three advantages of using named constants instead
of literals. What are they?
2. The following program attempts to change the value of a constant.
Copy the program and run it to see what error message is produced.
class BadConstantUse
{
public static void main (String[] args)
{
final int TEST = 0;
TEST = 1;
System.out.println(TEST);
}
}
As you may have guessed by now, writing a perfect program on the first
try is an extremely rare occurrence, even for the most experienced of pro-
grammers. On the other hand, many common errors (or ‘bugs’, as they
are called) can be avoided or at least detected and corrected quickly if we
follow good programming practices. To help you develop good habits in
your programming, sections like this one containing suggestions for avoid-
ing errors and debugging (eliminating errors that do occur), appear at the
end of most chapters.
Avoiding Errors
The style in which a program is written can have a great deal of influ-
ence on the chances that the program will be correct. Here are some ideas
that should help to keep you out of trouble.
1. Use meaningful identifiers. Any identifiers that you use for naming
classes, variables, or constants should be chosen so that the purpose
of each item is clear to any reader, not just you.
44 CHAPTER 1. GETTING STARTED
2. Indent your statements. Note and follow the style used in the text
or a style suggested by your instructor. In any case, be clear and be
consistent.
6. Use named constants (declared with the final attribute) rather than
literals for most constant values in your programs. They can make
programs more readable, more reliable, and easier to modify.
Debugging
The errors that inevitably arise in programming can be put into a
variety of categories. Two such categories are syntax errors and logical
errors. A syntax error in English is a mistake in the grammar rules of the
language. Similarly, a syntax error in Java is a mistake in following the
rules of that language. A syntax error will be detected by the Java compiler
when it attempts to produce byte code. Syntax errors are, for this reason,
sometimes called compile-time errors. A program that contains a syntax
error will not be compiled. With a logical error, on the other hand, the
program can be compiled but, when we attempt to run the program, it
does not perform its intended task.
In this section, we will be looking primarily at ways of dealing with
syntax errors but the elimination of such errors is only the first step in
debugging. Once the program is free of syntax errors and executing, check
it for logical errors by examining the output to see if, in fact, the program
is doing what you want it to do. We will be examining logical errors more
closely in later chapters. For now, here are some suggestions for handling
errors that appear during compilation.
1.11. AVOIDING ERRORS AND DEBUGGING 45
Exercises 1.11
1. Find any syntax errors or logical errors in each of the following En-
glish sentences.
(a) My younger sister was born two years before I were.
(b) The comma in this sentence is in the wrong place.
(c) This last parts is. easy.
2. Rewrite the following program using meaningful identifiers, inden-
tation, comments, and blank space to make the program easier to
understand.
int a = In.getInt();
System.out.println("What is your name?");
String b = In.getString();
System.out.println("Let me confirm that information.");
System.out.println("Your name is "+b
+" and you were born in "+a+".");}}
3. The following program contains five known syntax errors. Find them
and rewrite the program with the errors corrected.
class BadNews
/* a really terrible mess
{
public static void main (string[] args)
{
int i = 34.0, j = 2;
System.out.println(’Values are ’, i);
System.out.println(j);
}
}
class JustWriting
{
public static void main (String[] args)
{
System.out.print("Here we are ");
System.out.println("(finally),\n");
System.out.print("at the end\nof\n");
System.out.println("\nChapter 1");
}
}
2. Write a single Java statement that will print the following, exactly
as shown.
3. State the type of each legal constant. If the constant is illegal, explain
why.
(a) -5 (b) 37.
(c) ’\\’ (d) 25e2.0
(e) ’5’ (f) "5"
(g) "sample\n" (h) 133f
(i) ’’’ (j) ’example’
(k) 1350L (l) 15,472
48 CHAPTER 1. GETTING STARTED
6. Identify, with reasons, any identifiers that should not be used for Java
variables.
(a) 1stTime (b) Perimeter
(c) valueIn$ (d) beginning
(e) average height (f) case
10. Draw diagrams like those in Section 1.8 to illustrate the result of
executing the following statements.
(a) String s = "this"; (b) String s = "this";
String t = "that"; String t = s;
s = "that";
(c) String s = "this"; (d) String s = "this";
String t = "that"; String t = "that";
s = t; t = s;
11. Write and run an interactive program that asks the user to provide
a string, a character, an integer, and a floating point value. After
reading the values, the program should print each one on a separate
line with a suitable message for each line of output.
Real Jobs — Real People
Name: Mark A. Blake
Title: Vice President, Basis Exchange
Company: Basis 100
Education: B.Sc. Computer Science,
University of Edmonton (1989)
M.B.A., University of Western Ontario (1995)
Q: What was your first job after completing your computing science degree —
what did you like and not like about it?
A: My first job was as a Programmer/Analyst with Shell Canada. I worked
for the Mainframe Systems Group. The first project I worked on was to
automate the computer operations of a large IBM 3090 mainframe. The
objective was to reduce or eliminate the need for constant operator interac-
tion with the mainframe just to keep it running. It was fun and challenging
because you had to take a process where a human operator is reacting to
certain system events and then capture that process within a program. I
realized pretty quickly how a seemingly simple task like responding to an
error message is actually quite complex when you factor in all the scenar-
ios of how that message could be generated. One of my challenges was in
working for a large company it was sometimes difficult to see where I fit
in, i.e. how I was contributing to the “bottom line”.
Q: Why did you leave Shell Canada and what did you do next?
A: After three years with Shell, I saw a need for people that are able to act as a
liaison between the “business folks” and the “technology folks”. Therefore,
I decided to go back to school to do a Masters in Business Administration
(M.B.A.). I was also very interested in working and travelling internation-
ally. As part of my business degree, I spent one term studying at Keio
University in Yokohama, Japan.
Q: Where did you go after completing your M.B.A.?
A: I worked as an Information Technology (IT) Consultant with Deloitte Con-
sulting. Specifically, I helped companies assess their business strategy and
build an appropriate IT strategy to support it. I guess I became one of the
liaisons that I saw a need for while at Shell.
Q: What next?
A: After a short time with Deloitte, I was recruited by a small software com-
pany that specializes in stock exchange trading systems. My first position
50
was a Project Manager overseeing the system implementations in Palestine,
Nigeria, and Romania. Since then I have held a number of positions with
the company including postings in Australia and New York City. Recently,
the company was bought by Basis 100 Inc. and I was asked to assume my
current role overseeing the design, development, and sales of one of our
primary products — a bond trading system called BasisXchange.
Q: Based on your years of experience in designing and implementing systems,
what do you enjoy and what do you find difficult?
A: Fundamentally, I still enjoy the challenge of identifying an opportunity
for change or improvement and building a software solution to address
that need. In terms of difficulties, probably the biggest challenge I face is
working with clients to manage expectations about what a system can and
cannot do. I have realized that writing programs that do exactly what a
client wants is a very difficult task. The process of capturing requirements,
making sure they are complete, and then dealing with changes to those
requirements while you are writing code is profoundly difficult. From an
industry perspective, I think there is significant room for improvement in
the entire software design, development and delivery process.
Q: How do you think today’s programming languages, specifically Java, sup-
port this process?
A: I believe the platform independence and opportunity for reuse that Java
offers is part of the answer to the challenge of writing software that meets
the needs of the client. However, I think Java is only part of the solution.
I think we have to improve communication between the business and in-
formation systems groups. I believe it is the work that takes place before a
single line of code is written that contributes most to the eventual success
or failure of a project. Therefore, in addition to Java programming, proper
application of tools like the Unified Modeling Language (UML) for captur-
ing requirements will be an essential skill in any programmer’s toolkit.
Q: What career advice do you have for someone completing a computing sci-
ence degree today?
A: My advice is twofold: first, find a job in an industry that interests you.
Today, programming can be applied to almost any field. Therefore, whether
it is medicine, aviation or mining, try to work in an industry that you find
interesting and challenging. Second, try to understand and approach any
problem from a “business perspective” as well as a technical one. If you
can accomplish these two things I think you will be successful and will
ultimately find your career more rewarding.
51
Chapter 2
Example 1
To evaluate the expression +4 + 3 ∗ (5 − 6/2), Java would proceed as fol-
lows:
+4 + 3 ∗ (5 − 6/2) ⇒ 4 + 3 ∗ (5 − 6/2)
⇒ 4 + 3 ∗ (5 − 3)
⇒ 4+3∗2
⇒ 4+6
⇒ 10
We use the symbol ⇒ rather than = in evaluations of expressions to avoid
confusion with the use of = as an assignment operator. You may find it
useful to read ⇒ as “gives” or “evaluates to”.
2.1. BASIC ARITHMETIC OPERATIONS 55
In Example 1, all the values were of type int and the result was also
of type int. It is generally true that operations on values of the same
type produce results of that type. In particular, if we divide two integers,
the result is the integral quotient with any remainder being ignored. The
values of the divisor or dividend may be either positive or negative. The
sign of the quotient follows the usual rules of division.
Example 2
(a) 15/3 ⇒ 5 (b) 13/4 ⇒ 3
(c) 9/5 ⇒ 1 (d) 7/9 ⇒ 0
(e) 27L/10L ⇒ 2L (f) 9999L/10000L ⇒ 0L
(g) 7/(-3) ⇒ -2 (h) -15/4 ⇒ -3
(i) -5/(-6) ⇒ 0 (j) (-9)/(-5) ⇒ 1
Example 3
(a) 5 + 2.0 ⇒ 7.0 The result is a double.
(b) 3 * 4L ⇒ 12L The result is a long.
(c) 2.5f + 2.5 ⇒ 5.0 The result is a double.
If conversions are necessary, Java does not convert all the original
values in an expression prior to computing the result. Instead, it converts
values or intermediate results as necessary as the computation proceeds.
56 CHAPTER 2. PROGRAMS THAT CALCULATE
Example 4
(a) 1+1.0/2 ⇒ 1+0.5 ⇒ 1.5
Example 5
(a) 1.0/0 Result is Infinity
Example 6
(a) 7 % 3 ⇒ 1
(b) 12 % 15 ⇒ 12
(c) -20 % 7 ⇒ -6
(d) 5.9 % 1.2 ⇒ 1.1
(e) 8 % 0 Throws an ArithmeticException
(f) 0.8 % 0.0 ⇒ NaN
Exercises 2.1
1. Evaluate each valid expression and state the type of the result. If
the expression is invalid, give the reason. Use a decimal point in
your answer if the result is a floating point value (either float or
double). For example, use 3.0 rather than 3 to write a floating point
result whose value is three.
58 CHAPTER 2. PROGRAMS THAT CALCULATE
2. Evaluate.
(a) 17 % 5 (b) 23 % 10
(c) 20 / 3 + 20 % 3 (d) -10 % 2 + 1 / 2
(e) -7%(-2)/5 (f) 2.7%4
(g) 7 % 1.5 (h) 2L + 5.7f % 1.2f
(i) 1.5 % (3/4) (j) 2 - 5/0.0
(k) (-7)%(-3) (l) (-5)%(-1.5)
1 / 8 + 6 / 7⇒0 + 0⇒0
2.2. ASSIGNING AND PRINTING EXPRESSIONS 59
In the previous chapter, we saw how we could give values to variables using
assignment statements of the form
<identifier> = <value>;
As you might expect, assignment statements can be extended to give values
of expressions to variables by using statements of the form
<identifier> = <expression>;
In carrying out such assignments, the expression on the right is eval-
uated and then the resulting value is stored in the memory location of the
variable on the left. Of course, this will destroy any previous value that
may have been stored in that location.
The type of the expression on the right and that of the variable on the
left are usually identical but, if they are not, then the rules that we saw in
the last chapter for making conversions apply here also.
If we are working with values of various types, we may want to use
the operation of performing a cast that we studied in the last chapter. A
cast operator is applied to the value to the immediate right of the cast. A
cast operator has a higher precedence than either *, /, or %. Thus, if it is
combined with other operations and we want it to apply to more than one
value in an expression, we should use parentheses.
Example 1
(a) (int) 5.7 + 8.9 ⇒ 5 + 8.9 ⇒ 13.9 ( a double value)
(b) (int) 5.7 + (int) 8.9 ⇒ 5 + 8 ⇒ 13 (an int value)
(c) (int) (5.7 + 8.9) ⇒ (int) (14.6) ⇒ 14 (an int value)
(d) (double) (1/2) ⇒ (double) 0 ⇒ 0.0 (a double value)
(e) (double) 5/0 ⇒ 5.0/0 (produces positive infinity)
(f) (double) (5/0) (throws an ArithmeticException)
60 CHAPTER 2. PROGRAMS THAT CALCULATE
Example 2
Suppose that the following declarations have been made:
int m = 3, n = 4;
Statement Output
System.out.println("m: " + m); m: 3
System.out.println(m + n); 7
System.out.println("m" + "n"); mn
System.out.println("m + n: " + m + n); m + n: 34
System.out.println("m + n: " + (m + n)); m + n: 7
System.out.println("mn: " + m * n); mn: 12
System.out.println(m + n + "m + n"); 7m + n
System.out.println(m + n + (m + n)); 14
1 Since the + operator has different meanings in different contexts, we say that the
operator is overloaded. Although many kinds of operator overloading are seen in some
programming languages, this is the only form that it takes in Java.
2.2. ASSIGNING AND PRINTING EXPRESSIONS 61
Exercises 2.2
1. Find the value of each expression. Use a decimal point in your answer
if the result is a floating point value.
(a) (int) 1.8 * 0.6 (b) (int)(2/0.9)
In addition to the basic arithmetic operators, Java has two useful opera-
tors that increase or decrease the value of a variable by one. The increment
operator, ++, adds one to a variable while the decrement operator, --, sub-
tracts one from a variable. Both operators can be used in two forms: prefix
form, with the operator preceding its operand and postfix form, with the
operator following its operand.
Example 1
The statements
n++; and ++n;
both have the same effect as the statement
n = n + 1;
The statements
n--; and --n;
both have the same effect as the statement
n = n - 1;
The difference between the prefix and postfix forms only appears when
these operators are combined with other operators. In this text, we will
never use expressions that combine increment or decrement operators with
any others so, if you do not read the rest of this section, it will not cause
any difficulties. If, however, you are curious about the difference, read on.
When an expression containing an increment or decrement operator
is encountered, two things occur: the value of a variable is increased or
decreased and the expression is evaluated. The difference between prefix
and postfix forms is the order in which these operations are performed:
1. If a variable has a prefix operator, the variable is incremented or
decremented first and this new value is used as the value of the ex-
pression.
2.3. INCREMENT AND DECREMENT OPERATORS 63
Example 2
Suppose that we have the declaration:
int i = 2, j = 3;
If we then write the statement
i = (j++ + 4) * ++j;
evaluation proceeds as outlined in the following table:
Exercises 2.3
1. Suppose that the following declarations have been made:
int i = 3, j = 2, k = 5;
Using these starting values in each part, find the value of each variable
after the given statements have been executed.
64 CHAPTER 2. PROGRAMS THAT CALCULATE
Example 1
Assignment can also be combined with the other four basic arithmetic
operators.
-= *= /= %=
In each case, the effect is similar to that seen with +=, as illustrated in the
next example.
66 CHAPTER 2. PROGRAMS THAT CALCULATE
Example 2
(a) The statement x -= y + 1; is equivalent to x = x - (y + 1);
Example 3
The statement
i = j = k = 1;
first assigns the value 1 to k, then the value that has been assigned to k is
assigned to j, and finally the value that has been assigned to j is assigned
to i. Thus, the given statement is equivalent to
i = (j = (k = 1));
Example 4
Suppose that i, j, and k are int variables with values 1, 2, and 3 respec-
tively. Then the statement
i /= j -= k + 2;
would be evaluated as follows:
i /= j = j - (k + 2);
i /= j = 2 - (3 + 2);
2.4. MORE ASSIGNMENT OPERATORS 67
i /= j = -3;
i = i / (j);
i = 1 / (-3);
i = 0;
The final values of i, j, and k would be 0, -3, and 3 respectively.
The next table summarizes the precedence rules for the operators that
we have seen so far. Operators with higher precedence appear above those
with lower precedence. Parentheses can be used to alter the order of eval-
uation.
Operator Operation
++ -- increment, decrement
+ - unary plus, minus
(<type>) cast to <type>
* / % multiplication, division, remainder
+ - addition/concatenation, subtraction
= += -= *= /= %= assignments
In trying to understand how assignment operators behave, it may be
helpful to explore the process that occurs in the evaluation of a Java ex-
pression. In many ways, assignment operators are like arithmetic operators.
An expression containing a mixture of arithmetic and assignment operators
will be evaluated using the precedence rules that we have noted previously,
with assignments having the lowest precedence. However, when Java eval-
uates an assignment expression, it also carries out an assignment of a value
to a variable. The value of an assignment expression is the value that is
assigned to the variable. The actual assignment process is simply a side
effect of the evaluation of the expression.
Example 5
The sequence of statements
int i = 4, j = 2;
System.out.println(i += j = 5 + 3 * 4);
is perfectly valid.2 To perform the println, Java must first evaluate its
2 Although the code is valid, using a complex and possibly confusing expression like
the one shown in the println statement is not recommended. In programming, you
should always strive to maximize clarity, not to confuse your readers.
68 CHAPTER 2. PROGRAMS THAT CALCULATE
argument. The steps in the evaluation of that expression are shown in the
following table.
Exercises 2.4
1. Suppose that x is a double variable whose value is 3.0 just before
each statement is executed. Find the value of x after execution of the
statement.
(a) x *= 2; (b) x += x;
(c) x *= 2/3; (d) x /= 15/6;
(a) i = j = k; (b) i = j += k;
We saw in the previous chapter that it was possible to convert values be-
tween numerical forms and characters by using an assignment statement,
possibly with a cast. It is also possible (and often useful) to do arithmetic
involving char values.
Before we start doing arithmetic with characters, let us explore some
aspects of the relationship in Java between integers and characters. Recall
that all Java characters are stored as 16 bits using the Unicode encod-
ing system. As an example, the character ’A’ is stored as the pattern
0000 0000 0100 0001. If we consider this as a base 2 numeral, it has
a base 10 value of 1 × 26 + 1 × 20 = 64 + 1 = 65. If we were to write
int n = ’A’; then Java would assign the value 65 to n. The assignment
statement copies the bit pattern for ’A’ into the location reserved for n.
Although the pattern of the bits does not change, the way that Java inter-
prets the bit pattern does change — from the char value ’A’ to the int
value 65.3
In Unicode, the letters of the alphabet have been assigned codes for
which the numeric values are sequential. Thus the numeric value corre-
sponding to ’B’ is 66, that for ’C’ is 67, and so on. The lower case letter
characters: ’a’, ’b’, ’c’, . . . also have sequential numeric values (from 97
to 122). Using these properties, we can alter characters using arithmetic
operators.
3 See Appendix C for more information about representation of numbers using differ-
ent bases and the relationships between characters and integers.
70 CHAPTER 2. PROGRAMS THAT CALCULATE
Example 1
If we have created a char value c by the declaration
char c = ’A’;
then the statement
c++;
will change the value of c to ’B’.
Example 2
The statements
char c = ’A’;
c = c + 1;
will produce an error message because the expression c + 1 combines a
char and an int to produce an int result. The assignment to the char
variable c is an attempt to convert an int to a char — a narrowing con-
version. We can correct the problem by writing the assignment statement
in the form
c = (char)(c + 1);
The numeric values of the encodings for the characters ’0’, ’1’, . . . ,
’9’ are not 0, 1, . . . , 9 but they are sequential values. We can use this
fact to convert between the char form of a digit and the numerical value
of the digit. The next example shows how this can be done.
Example 3
Given the statement
char c = ’7’;
then the statement
int i = c - ’0’;
will assign to i the value 7 because the difference between the encoding of
’7’ and the encoding of ’0’ is 7.
2.5. ARITHMETIC AND CHARACTERS 71
Exercises 2.5
1. State the integer value of each expression.
(a) ’E’ - ’A’ (b) ’5’ - ’0’
4. Write a Java program that will first prompt the user for an upper case
letter of the alphabet, read the letter supplied by the user, and then
print the letter along with its position in the alphabet. For example,
if the input is ’D’, the program should print
D is at position 4 in the alphabet.
72 CHAPTER 2. PROGRAMS THAT CALCULATE
Math.abs
The method Math.abs determines the absolute value of an expression.
The expression on which Math.abs operates is called the argument of the
method. To use the method, the argument is enclosed in parentheses,
just as it is in a function in mathematics. The argument can be of any
primitive type other than boolean. The type of value returned by the
method corresponds to the type of its argument. If the argument is of type
int, long, float, or double, the method will return a value of that type.
If the argument is of a type narrower than int, the method will return a
value of type int.4
Example 1
(a) Math.abs(-4) ⇒ 4
(b) Math.abs(2.7f) ⇒ 2.7f
(c) Math.abs(-8.2e-4) ⇒ 8.2e-4
(d) Math.abs(-456L) ⇒ 456L
Math.sqrt
The Math.sqrt method returns the positive square root of the argu-
ment given to it. The method will take a numerical value of any type as
argument. It returns a value of type double. If the method is given a
negative value, it will return the value NaN (Not a Number).
Example 2
(a) Math.sqrt(4) ⇒ 2.0
(b) Math.sqrt(7.0) ⇒ 2.6457513110645907
(c) Math.sqrt(-5.0) ⇒ NaN
Math.pow
To calculate small positive powers of numbers, it is most efficient to
simply
√ use the multiplication operator, *. In addition, to calculate x0.5 =
x, it is best to use the Math.sqrt method. However, to calculate other
powers, we can use the method Math.pow that will evaluate xy . The method
always returns a double value, no matter what types the arguments are. A
call of the form Math.pow(a,b), if a and/or b are not double values, will
first determine the double equivalent of a and b. It will then determine
and return the value of ab as a double value. If the exponent is not an
integer and the base is negative, the method returns NaN.
Example 3
(a) Math.pow(8,1.0/3.0) ⇒ 2.0
(b) Math.pow(2,10) ⇒ 1024.0
(c) Math.pow(-32.0,0.2) ⇒ NaN
(d) (int)Math.pow(100,0.25) ⇒ 3
74 CHAPTER 2. PROGRAMS THAT CALCULATE
Math.max, Math.min
The method Math.max takes two arguments and returns the larger of
the two. As with Math.abs, the arguments to Math.max can be of type
int, long, float, or double. If the two arguments are of the same type,
the method will return that type; if they are of different types, the method
will return a value of the wider type. The method Math.min returns the
minimum of two values. It operates in a way exactly analogous to that
described for Math.max.
Example 4
(a) Math.max(-3,2) ⇒ 2
(b) Math.max(3.4f,5.3f) ⇒ 5.3f
(c) Math.max(7,3L) ⇒ 7L
(d) Math.max(-9e-4,-1.1e-6) ⇒ -1.1e-6
(e) Math.min(8,7) ⇒ 7
(f) Math.min(1.5f,2.0) ⇒ 1.5
(g) Math.min(-4.7,-9.3) ⇒ -9.3
(h) Math.min(18,6.7) ⇒ 6.7
Example 5
(a) Math.round(2.7) ⇒ 3L (b) Math.round(4.5f) ⇒ 5
Example 6
The following program reads and rounds a double value to the nearest
hundredth, a precision specified by the constant value of that name.
class Roundoff
{
public static void main (String[] args)
{
final double PRECISION = 0.01; // rounding precision
double inValue; // value to be rounded
double roundedValue; // value after rounding
Math.random
The Math.random method takes no argument. It returns a double, a
random value x in the interval 0 ≤ x < 1. By combining this method with
other methods and operations, we can generate random values suitable for
a wide variety of applications. As an example, suppose that we wish to
simulate the throwing of a fair die — one for which each of the six faces
on the die is equally likely to turn up. We can do so by performing the
s c
following operations.
We can begin by calling the
method Math.random to gener- 0 1 2 3 4 5 6
ate a value distributed randomly
in the interval 0 ≤ x < 1.
Multiplying this by 6, we ex- s c
pand the range to get a value dis- 0 1 2 3 4 5 6
tributed randomly in the interval
0 ≤ x < 6.
Next, casting this result as an s s s s s s
int, we chop off any fractional 0 1 2 3 4 5 6
part to obtain a random integral
value in the range 0, 1, . . . , 5.
Finally, adding one gives us a s s s s s s
random integral value in the de- 0 1 2 3 4 5 6
sired range: 1, 2, . . . , 6.
All of these operations can be performed in one step with the statement
int dieRoll = (int)(6 * Math.random()) + 1;
Constants
In addition to its methods, the Math class also has two important
double constants: Math.PI and Math.E. The constant Math.PI gives the
value of π = 3.14159 . . . to 16 significant digits while Math.E gives the
value of e = 2.71828 . . . to 16 significant digits.
Trigonometric Methods
The class has a number of trigonometric methods, including Math.sin,
Math.cos, Math.tan, Math.asin, Math.acos, and Math.atan. Java, like
2.6. USING MATH METHODS 77
Example 7
(a) sin 60◦ = 0.866025 . . . Math.sin(Math.PI/3) ⇒ 0.866025...
(b) cos 5◦ = 0.996194 . . . Math.cos(5*Math.PI/180) ⇒ 0.996194...
(c) tan 45◦ = 1 Math.tan(Math.PI/4) ⇒ 1.0
(d) cos 180◦ = −1 Math.cos(Math.PI) ⇒ -1.0
(e) cot 0◦ is not defined 1/Math.tan(0) ⇒ Infinity
Example 8
π
(a) arccos 0 = Math.acos(0) ⇒ 1.5707963...
2
π
(b) arctan 1 = Math.atan(1) ⇒ 0.7853981...
4
√
3 π
(c) arcsin = Math.asin(Math.sqrt(3)/2) ⇒ 1.047197...
2 3
78 CHAPTER 2. PROGRAMS THAT CALCULATE
Example 9
This section has not given a complete listing of the methods of Java’s
Math class. A full list with notes on all the elements of the class can
be found at Sun’s web site (http://java.sun.com/). At the site, to locate
information on any feature of the Math class, follow links first to the Appli-
cation Programming Interface or API. The API is a collection of hundreds
of classes that add functionality of various kinds to Java. The classes of
the API are organized into groups called packages. The Math class is in
the package called java.lang (not in the package java.math). Once you
are at the index page of the API, you can either locate a class directly
by its class name or find it indirectly by first going to its package and
then finding the class within the package. At the time that this was writ-
ten, the page containing the index of the API for Java 2 was located at
http://java.sun.com/j2se/1.4/docs/api/.
Now that we can do mathematics in Java, we can write many useful
programs. The next example illustrates a program structure that we will
see frequently. The program obtains input, does some calculation, and
produces some output.
2.6. USING MATH METHODS 79
Example 10
For a simple pendulum, the length of time for one swing (the period) is
determined by the pendulum’s length and the force of gravity. If we assume
that gravity is constant anywhere on the surface of the earth, then the
period of a pendulum is determined by its length. The formula connecting
period and length is s
L
P = 2π
g
where the period, P , is measured in seconds, the length, L, is measured in
metres, and g is the acceleration due to gravity — 9.8 m/s2 . The following
program uses this formula to determine the period of a pendulum of a given
length.
class Pendulum
{
public static void main (String[] args)
{
final double G = 9.8; // acceleration due to gravity
Exercises 2.6
1. State the value and type of each expression.
(a) Math.abs(-5)-Math.abs(-7)
80 CHAPTER 2. PROGRAMS THAT CALCULATE
(b) Math.abs(-1e-1)+Math.abs(-2e-2)
(c) Math.sqrt(0.0064)
(d) Math.sqrt(Math.pow(2.7,2))
(e) Math.round(3.499) (f) Math.max(1.5e-2,0.095)
(g) Math.ceil(4.002) (h) Math.min(-5,1.0)
(i) Math.floor(7.99) (j) Math.ceil(-2.73)
(k) Math.pow(16,0.25) (l) Math.pow(4,-2)
(m) Math.round(1.49 + 0.1) (n) Math.round(1.49) + 0.1
4. Write a statement that will make the int variable result take on a
random value uniformly distributed over the given set.
7. (a) Write a statement that will assign the int variable dodecaRoll
a random value that could result from the rolling of a fair twelve-
faced die with faces numbered from one to twelve.
(b) Write a statement that will assign the int variable doubleRoll
a random value that could result from noting the sum obtained
by rolling a pair of standard, fair dice with faces numbered from
one to six.
8. Find the value of each expression.
(a) Math.sin(Math.PI/6) (b) Math.cos(Math.PI/3)
(c) Math.cos(0) (d) Math.atan(0)
(e) Math.tan(Math.atan(0.5))(f) Math.asin(Math.sin(1))
10. Write an expression that could be used to find the value of log10 x.
11. In this section, we showed a program that rounded a value to the
nearest hundredth. How would that program have to be modified to
round values to
(a) the nearest tenth? (b) the nearest thousand?
12. Airport runways are often given numbers determined by the direction
in which planes travel as they move along the runways. The number
of a runway is found by taking the bearing in degrees (to the nearest
ten degrees) and dropping the final zero. For example, a runway with
a bearing between 265◦ and 275◦ would have a runway number of 27.
Write a program that asks the user for a bearing (from 0◦ to 360◦ )
and then determines the corresponding runway number.
13. Write a program that asks for a length of time in hours and then con-
verts this measurement to hours, minutes, and seconds. For example,
input of 38.47 should produce output similar to the following:
38.47 hours = 38 hours, 28 minutes, 12 seconds
82 CHAPTER 2. PROGRAMS THAT CALCULATE
Avoiding Errors
1. In evaluating expressions, the order in which operations are per-
formed is often critical. To be sure that operations are carried out
in the correct order (and to help make the order clear to the reader),
it is often a good idea to use extra sets of parentheses in complex
expressions. Casts, with their high precedence, are often causes of
errors of this sort. For example, the expression (int) x*y will be
evaluated by first casting x to an int and then multiplying the result
by y.
2. Another error that arises frequently with order of operations involves
expressions of the form a/b*c. Since multiplication and division have
equal precedence and are evaluated from left to right, such an expres-
sion will be evaluated as if it were written in the form (a/b)*c —
not as a/(b*c). In mathematics, the meaning of an expression such
a
as bc is clear because the expression is two-dimensional. The corrre-
sponding expression in Java, a/(b*c) is one-dimensional and hence
more difficult to interpret correctly.
3. Division operations can often cause errors because integer divisions
produce integer values. For example, the expression (3/4)*8 has the
value zero, not six. A cast is often a good way to get around such
problems. The value of ((double)3/4)*8 is six (as a double).
4. Because each numerical value is stored in a fixed number of bits
while there are infinitely many real numbers, not all numbers can be
represented exactly in a computer. This can cause a variety of errors.
(a) Roundoff errors occur when a decimal value cannot be repre-
sented exactly. We see this all the time when we try to write a
fraction like 31 as a decimal. Simply writing digits will never give
us the exact value of the fraction, no matter how many digits
we write. You can also see this with a calculator if you use one
to evaluate the expression: 3(1/3 + 100 − 100). In theory, the
2.7. AVOIDING ERRORS AND DEBUGGING 83
5. For virtually all the problems that you will be asked to solve in this
book, Java’s integer and floating point types should be adequate,
if used carefully. If you have a need for representations of larger
values, Java provides the classes BigInteger and BigDecimal in the
java.math package. Features of the BigInteger class are discussed
on page 266.
Rather than use this formula directly, it is a much better idea to first
calculate the value of the semi-perimeter, s, of the triangle
a+b+c
s=
2
and then use this to determine the area
p
A = s(s − a)(s − b)(s − c)
Debugging
with some text that identifies the source of the value being printed.
For example, to trace the value of the variable sum at some point,
you could insert the following tracing statement in your program.
System.out.println("At A, sum = " + sum);
The phrase “at A” identifies the location in the program. If sum
changes value at some other point, you may want to insert another
tracing statement that prints At B, sum = ...
2. Many systems have debuggers — programs that can be used to trace
variables easily. The operation of debuggers varies widely but any
debugger should have facilities to
• stop a program at a specified point and then restart it
• display the values of selected variables at the point at which a
program was stopped
• step through a program one statement at a time
If your system does have a debugger, it is well worth your while to
learn how to use at least the basic features.
Exercises 2.7
1. Evaluate.
(a) 6 / 4*2
(b) 2 * 3/2
(c) (int)2.7*1.8
(d) (int)2.7*(int)1.8
(e) (int)(2.7*1.8)
2. For each expression, determine its mathematical value and then run
a Java program that prints the value of the expression. Explain any
differences.
(a) 3.0*(1.0/3.0 + 100.0 - 100.0)
(b) (5E305 + 7E307)*10
(c) 18 + 1E18 - 1E18
(d) 1E18 - 1E18 + 18
86 CHAPTER 2. PROGRAMS THAT CALCULATE
int i = 3, j = 4, k = 2;
Using these starting values in each part, find the value of each variable
after the given statement has been executed.
(a) j = ++i * k--; (b) i = --j + k/2;
(c) k = i-- - j++; (d) j = (2*i++)%(++k + 1);
(e) i += j -= --k; (f) i *= j /= k++;
5. Evaluate.
(a) Math.round(Math.sqrt(20))
(b) Math.ceil(-4.6)
(c) Math.min(0.0024,1.2e-3)
(d) Math.pow(0.5,-4)
(a) If, before execution of the segment, x contained the value 7 and
y contained the value 4, what value would each have after the
segment was performed?
(b) Rewrite the segment so that it performs the intended task cor-
rectly.
9. Write a program that reads three double values and computes their
mean, rounded to two decimal places.
10. The current deficit of Arrakis is 47 000 000 grods and it is estimated
that the deficit will increase by 4.5% in each of the next two years.
Write a program that will find the estimated deficit in these two years,
rounded correctly to the nearest million grods.
11. Write a program that asks the user for a three-digit number, finds
the sum of the digits of the number, and then prints both the number
and its digit sum.
Projects
12. As you are probably aware, the date of Easter can vary widely from
year to year. The Council of Nicæa in the year 325 decreed that
Easter should be held on the first Sunday following the full moon
that occurs on or after March 21, the usual day of the vernal equinox.
Because the date depends on solar, lunar, and calendar cycles, it is
not easy to find an arithmetic procedure that determines the correct
date for any given year. The one given below (for the Gregorian
calendar) was created by J. M. Oudin in 1940. In the equations, y
represents the year, m represents the month (3 for March and 4 for
April), and d represents the day of the month. All variables are
2.8. REVIEW EXERCISES 2 89
y
p=
100 y
q = y − 19
19
p − 17
r=
25
p p−r
s=p− − + 19q + 15
4 3
s
s = s − 30
30
s s 29 21 − q
s = s− 1−
28 28 s+1 11
y p
t = y+ +s+2−p+
4 4
t
t = t−7
7
u = s−t
u + 40
m=3+
44
m
d = u + 28 − 31
4
Write a program that asks the user for a year and then gives the
month and day of Easter in that year.
Note: All results are rounded to one decimal place for printing. Your
program must handle the unit conversions as well as printing the
planet’s mass in terms of 1021 kg. Assume that this will always pro-
duce reasonable results. Assume that the planet is perfectly spherical.
Chapter 3
Decision Making
Example 1
This program demonstrates creating and printing of boolean variables.
class BooleanOutput
{
public static void main (String[] args)
{
boolean x = false;
boolean y = true;
System.out.println("x: " + x + " y: " + y);
}
}
This program will print
x: false y: true
Example 2
(a) The expression 2 < 3 has the value true.
(b) The expression -6.3 > 1.75 has the value false.
3.1. DECISIONS AND RELATIONAL EXPRESSIONS 93
Expressions like the ones shown in this example are called relational
expressions because their values depend on the relationship between other
values. The example used the relational operators < and >. Java has a
total of six relational operators. They are shown in the following table.
Relational Operator Meaning
< is less than
<= is less than or equal to
> is greater than
>= is greater than or equal to
== is equal to
!= is not equal to
Operators that are constructed from two characters must not have
embedded blanks and must be in the order shown. The == operator is a
frequent cause of errors in Java programs; people often omit the second
equals sign. The operator is written as it is to distinguish it from the
assignment operator.
Values of any of the primitive types can be compared in relational
expressions. Values of type boolean can only be compared for equality
or inequality; comparison of the relative sizes of boolean values is not
meaningful.
Values of type char are ordered according to a collating sequence.
Java’s collating sequence is determined by the ordering of characters in the
Unicode encoding system. A character that precedes another in the collat-
ing sequence is considered to be less than the other. Usually, comparison
of characters is used with alphabetic characters and the results are what
you would expect. The Unicode encodings for letters follow alphabetic or-
der so that ’A’ < ’B’ < . . . < ’Z’ and ’a’ < ’b’ < . . . < ’z’. One must,
however, be careful with these comparisons as all upper case letters have
encodings that are smaller than any of the lower case letters. In addition,
accented letters such as é, ü, or ç have encodings that are larger than any
unaccented letters. Life is never simple.1
Luckily, it is rare that a programmer needs to be concerned with the
details of Unicode encodings of characters. For most situations, all that
you need to know is that
• ’0’ < ’1’ < ’2’ < ...< ’9’
• ’A’ < ’B’ < ’C’ < ...< ’Z’
1 More details can be found in Appendix C.
94 CHAPTER 3. DECISION MAKING
Example 3
(a) The expression
’T’ < ’t’
has the value true because upper case letters have smaller Unicode
encodings than lower case letters.
(b) To evaluate the expression
’#’ < ’+’
we need to know the Unicode encoding values of these characters.
These values can be found in the table on page 600. From the table,
we can see that the numerical value of the encoding of ’#’ is 35 while
that of ’+’ is 43. Thus the expression has the value true because
35 < 43.
Example 4
The expression 2.0 + 3 < 2 ∗ 3 would be evaluated as follows:
2.0 + 3 < 2 ∗ 3 ⇒ 2.0 + 3 < 6
⇒ 5.0 < 6
⇒ true
3.1. DECISIONS AND RELATIONAL EXPRESSIONS 95
Exercises 3.1
1. For each legal expression, state its value. For each illegal expression,
state the reason that it is illegal.
(a) (2 + (-5)) != 3 (b) ’m’ < = ’p’
(c) ’Q’ == ’q’ (d) ’*’ < ’*’
(e) 8.23 =< 8.2300 (f) (7 / 3) = 2
(g) false == 0 (h) (25 % 4) >= 1
class BooleanVariables
{
public static void main (String[] args)
{
boolean perhaps, maybe;
perhaps = 4 < 5;
maybe = -17 % 4 == 1;
System.out.println("perhaps: " + perhaps);
System.out.println("maybe: " + maybe);
}
}
5. Because of the ways that Java’s floating point values are stored, it
may happen that numbers which should, in theory, be equal are, in
fact, only approximately equal. Thus, it is not a good idea to test
for exact equality of two floating point values. A better idea is to see
if the numbers differ by less than some desired value. For example,
given two double values x and y, the expression
(a) Write an expression that will be true if and only if x and y differ
by no more than one one-millionth of the value of x.
(b) Why is it a good idea to have the test dependent on the magni-
tude of one of the values being compared?
s1 s2
- "Hello"
3.2. COMPARING STRINGS 97
Because both s1 and s2 refer to strings with identical values, the Java
compiler tries to save space by only allocating memory for one copy of the
string "Hello" and having both s1 and s2 refer to this location.
On the other hand, if we were to write
String s1 = "Hello";
String s2 = "He";
s2 += "llo";
then both s1 and s2 will still refer to a string with the value "Hello" but
now, because we have constructed the two strings in different ways, the
Java compiler will probably not realize that they are the same2 and will
create two distinct copies of the string. The next diagram illustrates the
resulting situation.
s1 s2
- "Hello" - "Hello"
equals
To test whether or not two string variables are referring to equal string
objects, we can use the method equals from the String class. The method
tests two strings for equality and returns the value true if the strings are
identical and false otherwise. This method is used in a manner that is
2 We say “probably” because some Java compilers are better than others at detecting
this sort of thing.
98 CHAPTER 3. DECISION MAKING
somewhat different from that used for the methods of the Math class that
we saw in Chapter 2. If s1 and s2 are both references to strings, then the
expression
s1.equals(s2)
will have the value true if and only if the values of the two strings are
identical.
Example 1
Given the declaration
String s = "Same";
then
(c) s.equals("Same ") will return false because of the blank in the
string "Same "
compareTo
The lexicographic order of two strings is, essentially, their dictionary
order; it is based on the collating sequence of the characters in the strings.
To determine the lexicographic order of a pair of strings, we can use
the method compareTo from the String class. In comparing strings, the
method proceeds from left to right, character by character, as long as cor-
responding characters are identical. This process can terminate in one of
three ways:
• The end of both strings is reached with all characters equal; the
strings are identical.
• The end of one string is reached but the other is not; the shorter
string precedes the longer string.
The method compareTo returns an integer value that indicates the lexico-
graphic ordering of two strings. If s1 and s2 are both references to strings,
then the expression
s1.compareTo(s2)
has the value zero if s1 is identical to s2, a negative value if s1 precedes
s2, and a positive value if s1 follows s2.
Example 2
(a) "cab".compareTo("car") < 0 because ’b’ < ’r’
(b) "Car".compareTo("car") < 0 because ’C’ < ’c’
(c) "27".compareTo("186") > 0 because ’2’ > ’1’
(d) "car".compareTo("cart") < 0 because "car" is only the first part
of "cart"
The compareTo method can be used to implement any of the six rela-
tional operators (<, <=, >, >=, ==, or !=) by writing expressions of the form
<string1 >.compareTo(<string2 >) <op> 0
where <op> is one of the six relational operators. For example, if s1 and
s2 are both references to strings, then the expression
s1.compareTo(s2) <= 0
has the value true if and only if the string s1 precedes or is identical to
the string s2.
Exercises 3.2
1. For each of the following pairs of strings, state, with reasons, which
string precedes the other.
(a) "cat" and "dog" (b) "cat" and "Cathy"
(c) "X " and " X" (d) "cab" and "CAR"
100 CHAPTER 3. DECISION MAKING
(e) "XX" and "X X" (f) "XY" and "XY "
(a) "for".compareTo("fore")
(b) "fore".compareTo("force")
(c) "force".compareTo("Force")
(d) "Force".compareTo("Farce")
(e) "Farce".compareTo("Fare")
(f) "Fare".compareTo("far")
(g) "far".compareTo("far")
(h) "far".compareTo("far ")
(a) s1 is identical to s2
(b) s1 precedes s2
(c) s1 is identical to or follows s2
(d) s1 does not follow s2
3.3. THE IF STATEMENT 101
if (<boolean expression>)
<statement1 >
else
<statement2 >
?
H
HH
false Evaluate H true
H
H
H
HH <expression>
H
H
? H ?
Execute Execute
<statement2 > <statement1 >
?
Example 1
This program compares two integers supplied by the user and prints an
appropriate message.
class IfDemo
{
public static void main (String[] args)
{
System.out.println("Please give one integer");
int first = In.getInt();
System.out.println("and a second");
int second = In.getInt();
if (first == second)
System.out.println("The values are equal");
else
System.out.println("The values are not equal");
}
}
If a user supplies two equal values to the program, the output will be
The values are equal
?
H
H
H
Evaluate HHH true
HH <expression>
HH
H
H ?
Execute
false <statement>
Example 2
The following statement warns a user if a value of mark indicates a failure
on a test.
if (mark < PASSING_MARK)
System.out.println("Mark indicates a failure");
If the value of mark is not less than the mark required to pass, no message
will be printed.
104 CHAPTER 3. DECISION MAKING
Example 3
Each of the following statements has the same effect: the value of x will be
printed if and only if x > 0.
(a) if (x > 0)
System.out.println("x = " + x);
(b) if (x > 0)
System.out.println("x = " + x);
else
;
(c) if (x <= 0)
;
else
System.out.println("x = " + x);
Example 4
The following fragment checks on the sign of a value. If it is negative, the
fragment prints a warning message and makes the value positive.
if (value < 0)
{
System.out.println("Negative value set to positive");
value = Math.abs(value);
}
3.3. THE IF STATEMENT 105
Exercises 3.3
1. (a) Rewrite the following program using the indentation style shown
in the text.
class VoterTest{public static void main(String[]args
){final int VOTING_AGE=18;int age;boolean eligible;
age=In.getInt();if(age>=VOTING_AGE)eligible=true;
else eligible=false;if(eligible)System.out.println(
"Eligible to vote");else System.out.println(
"Not eligible to vote");}}
(b) What would the program print if it were given input of 19?
(c) Replace the first if statement with a single assignment to the
boolean variable eligible.
(a) Add one to the value of zeroCount if the variable total has the
value zero.
(b) Write an appropriate message depending on whether or not the
value of the int variable n is a perfect square.
(c) Add one to the value of pageCount if the variable lineCount is
greater than pageLength.
(d) Set the value of the boolean variable leftSide to true if the
int variable page is even, and to false if page is odd.
3. Write the simplest statement that has the same effect as the given
statement.
(a) if (x != y)
;
else
System.out.println("Values are equal");
(b) if (answer <= maxValue)
;
else
System.out.println("Answer is wrong");
106 CHAPTER 3. DECISION MAKING
4. Write a program that prompts the user for two integers, determines
whether or not the first number is a multiple of the second, and then
prints both numbers and an appropriate message.
5. Write a program that reads a double value and prints both the
number and its absolute value without using the built-in method
Math.abs. The definition of the absolute value of x, written as |x|, is
x if x ≥ 0
|x| =
−x if x < 0
6. Write a program that solves an equation of the form ax+b = 0 by first
prompting the user for the values of a and b, then solving the equation
and printing the results. The program should take appropriate action
if a is zero.
Notice that the truth table for p || q has the value true not only if
exactly one of them is true but also if both p and q are true. This may
seem strange at first, but we often use the word “or” in this sense. For
example, if one is asked, “do you take cream or sugar with your coffee?” it
is reasonable to reply, “yes, I take both.” Taking “cream or sugar” includes
the possibility of taking both.
If an expression contains more than one boolean operator, then, as
with arithmetic expressions, precedence rules determine the order in which
operations are performed. If we do not use parentheses, then the precedence
order of the three boolean operations is
!
&&
||
As with arithmetic operations, the precedence rules can be overridden by
using parentheses.
Example 1
!(true || false) && true ⇒ !true && true
⇒ false && true
⇒ false
Operator Operation
++ -- increment, decrement
+ - unary plus, minus
! boolean not
(<type>) cast to <type>
* / % multiplication, division, remainder
+ - addition/concatenation, subtraction
< <= > >= relational ordering
== != relational equality, inequality
&& boolean and
|| boolean or
= += -= *= /= %= assignments
108 CHAPTER 3. DECISION MAKING
Operations with equal precedence are performed from left to right with the
exception of assignment operations which (as we have already seen) are
performed from right to left.
Example 2
4 < 5 || ’X’ > ’Y’ ⇒ true || false
⇒ true
Example 3
Lazy evaluation can be quite useful in programming. Suppose that we want
to perform some action if a is divisible by b. If we wrote
if (a % b == 0)
then an error would occur if b were zero and an ArithmeticException
would be thrown. To guard against this, we could write
if (b != 0 && a % b == 0)
Now, if b were zero, the first half of the expression would be false and Java
would not look at the second half of the expression so no error would occur.
be false. Thus, the negation of this statement can be written in the form
“This book is not boring or this course is not useless.”
If we let b represent “This book is boring” and we let u represent “This
course is useless”, then, from the above argument, the statement “it is not
true that this book is boring and this course is useless” could be written
as !(b && u). On the other hand, the statement “this book is not boring
or this course is not useless” could be written as !b || !u. Since these
statements are equivalent, then
!(b && u) is equivalent to !b || !u
This is one of de Morgan’s laws. The other is formed by interchanging the
positions of && and ||. As an example of the second form, the statement “I
do not like green eggs or ham” means “I don’t like green eggs and I don’t
like ham.” Using the symbol ≡ to mean that two boolean expressions are
equivalent, we can write the two laws as follows:
!(p && q) ≡ !p || !q
!(p || q) ≡ !p && !q
Exercises 3.4
1. Evaluate each expression assuming that the following declarations
have been made.
boolean p = true, q = false, r = false, s = true;
(a) !p (b) p || q
(c) p && r (d) !(q && s)
(e) !q && s (f) s && !q
(g) p || !s (h) !p && !q
(i) s || (!q && r) (j) p == (q || r)
3. Write a boolean expression that will be true if and only if the int
variable i satisfies the condition 0 ≤ i ≤ 5.
p && q ≡ q && p p || q ≡ q || p
p && (q && r) ≡ (p && q) && r p || (q || r) ≡ (p || q) || r
p && (q || r) p || (q && r)
≡ (p && q)||(p && r) ≡ (p || q)&&(p || r)
p && p ≡ p p || p ≡ p
p && !p ≡ false p || !p ≡ true
p && true ≡ p p || false ≡ p
p && false ≡ false p || true ≡ true
p && (p || q) ≡ p p || (p && q) ≡ p
!(p && q) ≡ !p || !q !(p || q) ≡ !p && !q
!true ≡ false !false ≡ true
!!p ≡ p
p q p⊕q
true true false
true false true
false true true
false false false
We have seen that we can use if statements to choose between two alter-
natives. Frequently, however, we want to choose from among more than
two possibilities.
Example 1
If we want to write a statement based on the sign of a variable x, we could
write three if statements.
if (x < 0)
System.out.println("value is negative");
if (x > 0)
System.out.println("value is positive");
if (x == 0)
System.out.println("value is zero");
For any value of x, this solution involves three tests. We can increase
the efficiency of the code by nesting the statements. This involves placing
one if statement within another as shown in the next example.
112 CHAPTER 3. DECISION MAKING
Example 2
This improves the efficiency of the code in Example 1.
if (x < 0)
System.out.println("value is negative");
else
if (x > 0)
System.out.println("value is positive");
else
System.out.println("value is zero");
Now, if x is negative, only one test is performed while if x is positive or
zero, only two tests are performed. In no case is it necessary to perform
three tests.
Example 3
The if statement shown below efficiently determines the largest among
three values x, y, and z and assigns this value to largest.
if (x >= y)
// y eliminated - largest must be either x or z
if (x >= z)
largest = x;
else
largest = z;
else
// x eliminated - largest must be either y or z
if (y >= z)
largest = y;
else
largest = z;
There are many other ways that we could determine the largest of three
values but the one shown here involves the least work.
3.5. NESTED IF STATEMENTS 113
Example 4
This statement prints a comment that depends on the value of the char
variable grade.
if (grade == ’A’)
System.out.println("Excellent");
else if (grade == ’B’)
System.out.println("Good");
else if (grade == ’C’)
System.out.println("Average");
else if (grade == ’D’)
System.out.println("Fair");
else if (grade == ’E’)
System.out.println("Poor");
else
System.out.println("Invalid grade");
As well as noting the indentation style, note that the last line covers the
case of an invalid grade. You should always be sure that your code covers
such possibilities.
A: if (p)
if (q)
System.out.println("Path A-1");
else
System.out.println("Path A-2");
114 CHAPTER 3. DECISION MAKING
B: if (p)
if (q)
System.out.println("Path B-1");
else
System.out.println("Path B-2");
B*: if (p)
{
if (q)
System.out.println("Path 1");
}
else
System.out.println("Path 2");
B**: if (p)
if (q)
System.out.println("Path 1");
else
; // to "close off" the second if
else
System.out.println("Path 2");
Exercises 3.5
1. Simplify the following sequence by nesting so that the effect is the
same but fewer comparisons are required.
3.5. NESTED IF STATEMENTS 115
Decisions with many possible choices can be handled using nested if state-
ments but Java has another statement, the switch statement, that often
makes choosing from many alternatives an easier task.
Example 1
To see how a switch statement works, suppose that we are writing a pro-
gram that acts as a simple calculator. If at some point we have two numer-
ical values (operand1 and operand2) and a char variable operator with
possible values ’+’, ’-’, ’*’, or ’/’, then the following fragment will print
the value of the corresponding expression.
switch (operator)
{
case ’+’: System.out.println(operand1 + operand2);
break;
case ’-’: System.out.println(operand1 - operand2);
break;
case ’*’: System.out.println(operand1 * operand2);
break;
case ’/’: System.out.println(operand1 / operand2);
break;
}
still work correctly even if, at some later time, an extra case is added to it.
As others have noted, a little extra redundancy is often a good idea.
To guard against the possibility that no case matches the value of the
expression in the switch, we can (and should) include a default case that
contains statements to execute if there is no match. We can also have more
than one case value associated with the same statements. Both of these
ideas are illustrated in the next example.
Example 2
This switch statement assigns a grade based on a quiz that was scored out
of five.
switch (score)
{
case 5: grade = ’A’;
break;
case 4: grade = ’B’;
break;
case 3: grade = ’C’;
break;
case 2:
case 1:
case 0: grade = ’F’;
break;
default: System.out.println("Invalid score"
+ " - grade of ? assigned");
grade = ’?’;
break;
}
Exercises 3.6
1. Rewrite the following using the indentation style shown in the text.
4. Write a program fragment that prints, as a word, the value of the last
digit of the int variable number. For example, if the value of number
is 547, the fragment should print
3.7. AVOIDING ERRORS AND DEBUGGING 119
July 1, 2000
Avoiding Errors
1. Because of the way that floating point values are stored, it may hap-
pen that values that should be exactly equal are, in fact, only approx-
imately so. Thus, it is not a good idea to test for exact equality of
expressions that contain floating point values. A better idea is to see
if the values differ by less than some quantity. To see if x and y are
sufficiently close, we might use a test of the form |x − y| < where
(the Greek letter epsilon) is some small positive quantity. A problem
with this is that it applies the same test for closeness to both small
and large numbers. While we might consider small numbers to be
“close” if they differ by 0.001, we may consider very large numbers
to be “close” if they differ by only 100. To handle such differences, it
is a good idea to make our test of closeness depend on the magnitude
of the quantities being examined. A good scheme that employs this
principle is to use a test of the form |x − y| < × max(|x|, |y|).
if (x < 0);
System.out.println("Value is negative");
if (x < 0)
;
System.out.println("Value is negative");
if (value < 0)
System.out.println("Negative value made positive");
value = -value;
3.7. AVOIDING ERRORS AND DEBUGGING 121
Debugging
int m;
int n = In.getInt();
if (n > 0)
m = 1;
else if (n == 0)
m = 2;
System.out.println(m);
int m;
int n = Math.abs(In.getInt());
if (n > 0)
m = 1;
else if (n == 0)
m = 2;
System.out.println(m);
will still produce the same message even though it is now impossible
for n to be negative. The easiest way to get past this problem is
to initialize m to zero where we declare it (in the first line of the
fragment).
if ( ... )
if ( ... )
<statement1 >
else
<statement2 >
else
<statement3 >
Exercises 3.7
1. (a) In the section on avoiding errors, we suggested that a good test
for the closeness of two floating point variables x and y was to see
if |x − y| < × max(|x|, |y|). If x and y are two double variables
and EPSILON is a double constant representing , write a Java
expression that will be true if and only if x and y satisfy this
test.
(b) For the values of x, y, and EPSILON that follow, state whether
the expression in part (a) would be true or false.
i. x = 123.48, y = 123.77, and EPSILON = 0.01
ii. x = 0.000456, y = 0.000457, and EPSILON = 0.001
iii. x = 8.7658, y = 8.7651, and EPSILON = 0.001
iv. x = 12626.7, y = 12539.2, and EPSILON = 0.01
2. Assuming that x, a, and b are double variables and a < b, write the
simplest Java expression that will be true if and only if
(a) x is between a and b
(b) x is outside the interval between a and b
(c) x is either less than a or less than b
124 CHAPTER 3. DECISION MAKING
if (x < 0)
System.out.println("invalid");
else;
System.out.println("valid");
(a) For what value(s) of x will the word valid be printed? Justify
your answer.
(b) Rewrite the fragment so that the word valid is printed only if
x ≥ 0.
if (a > 0)
if (b < 0)
System.out.println(1);
else
System.out.println(2);
else
System.out.println(3);
2. For each legal expression, state its value. For each illegal expression,
state the reason that it is not legal.
(a) !(3 < 4.5) (b) ’r’ < ’q’
(c) !(17 / 5 = 3.4) (d) ’G’ != ’g’ || 1 + 2 <= 3
(e) 5 < Math.abs(3-2*5) < 10(f) ’6’ - ’2’ == ’4’
3. Given three strings s, t, and u, write a fragment that would print the
string that would be first if they were to be printed in lexicographic
order.
4. In Question 3, the ordering of the strings is not necessarily the order
in which they would be printed in a dictionary. Explain.
5. Study this program and then answer the questions that follow it.
class ChipChoice
{
public static void main (String[] args)
{
System.out.println("Preferred flavour?");
char flavour = In.getChar();
System.out.println("Preferred style?");
char style = In.getChar();
System.out.print(", crinkled");
else if (style == ’R’)
System.out.print(", regular");
System.out.println(" chips.");
}
}
What would the program print given each set of data as input?
(a) V (b) B
C R
(c) V (d) B
T C
(e) C (f) v
V r
6. How would you modify the program of the previous question so that it
would accept both upper case and lower case letters in the responses
from the user?
7. Rewrite as a single if statement in as concise a form as possible.
Assume that flag is of type boolean and n is of type int.
if (true)
if (!flag)
n = 1;
else
n = 0;
if (p && q)
System.out.println("Both true");
if (p && !q)
System.out.println("Only first true");
if (!p && q)
System.out.println("Only second true");
if (!p && !q)
System.out.println("Neither true");
11. One way of giving a direction is to simply use one of the letters N,
E, S, or W indicating the nearest compass point. Another way is to
give a bearing as a number from 0 to 359. A bearing of 0 corresponds
to N, a bearing of 90 corresponds to E, and so on. Write a program
that prompts the user for a bearing from 0 to 359 and prints the
corresponding letter of the compass point nearest to that bearing.
For example, input of 73 should produce output of E. Bearings half
way between two compass points should be taken to be either N or
S, as appropriate. Your program should make decisions efficiently.
12. Suppose that two line segments on a number line are represented by
the values of their endpoints: segment s1 is represented by left1 and
right1 while segment s2 is represented by left2 and right2. For
example, the values
left1 = -1
right1 = 4
left2 = 6
right2 = 9
would define the segments shown in the diagram.
q q q q
s1 s2
−1 0 1 2 3 4 5 6 7 8 9 10
Projects
14. Write a program that makes change for amounts less than one dollar.
Input to the program should be a positive integer less than 100, repre-
senting an amount of money, in cents. Output should be the original
amount of money together with a set of coins (quarters, dimes, nick-
els, cents) that could make up that amount. The program should
produce change containing the minimum number of coins required
for the given amount. The output should be in a natural, non-stilted
form. For example, input of 58 should produce output something like
58 cents: 2 quarters, 1 nickel, and 3 cents.
3.8. REVIEW EXERCISES 3 129
rather than
58 cents: 2 quarters, 0 dimes, 1 nickels, 3 cents.
130
with an email address and a Canadian bank account. Using CertaPay you
can, for example, pay for items that you buy on eBay from your bank
account and have the money deposited in the seller’s account in real time.
Q: That sounds as if it would be very exciting. Why are you not still with
them?
A: The banks bought us out and took over CertaPay from the original part-
ners. We were keen to expand the international capablities of the platform
but they wanted to focus on expanding the functionality for the Canadian
marketplace. The stimulating aspects of the project, particularly product
development, dried up.
Q: Give a brief description of your present job.
A: I currently provide internet marketing and product development service on
a contract basis to organizations in the financial services, natural resource
development, and government sectors.
Q: What does a typical day look like, if you have such a thing as a typical
day?
A: I don’t really have a typical day but most of my time is shared among
three areas: business development, liason with existing clients, and actual
project work.
Q: What are the best features of having your own business?
A: I have a great deal of freedom in picking and choosing my projects and the
money is better. Since I work from home, there is almost no overhead and
the dog likes it because he has company for most of the day.
Q: Are there any negative aspects to being on your own?
A: Not many. Occasionally, because my office is at home, I find myself work-
ing well beyond regular office hours. Also, I need to be conscious of the
necessity of keeping a full load of work.
Q: How does Java fit in to your area of interest?
A: It is very important. I recommend Java as the application language to any
client requiring a mission critical internet application.
131
Chapter 4
Repetition
In cooking, a recipe may tell us that, as long as there are lumps in a sauce,
we should stir it. We can make these instructions more Java-like by writing
them in the form
while (there are lumps)
give sauce a stir;
The idea here is that we repeatedly check for lumps and, if we find any,
stir the sauce to try to get rid of them.
We can illustrate this process as we did with an if statement, by using
a flow chart. Here is a flow chart for our cooking process. Perhaps you can
see from the diagram why we call this structure a loop. As long as there is
lumpiness, we follow the path that carries us to stirring and then back to
testing for lumps.
?
H
H
H
Any lumps? HH
Yes - Stir once
HH
H
H
H
No
?
The general form of Java’s while statement follows the same pattern
as the Java-like form we used for our cooking analogy:
while (<boolean expression>)
<statement>
Execution of a while statement is similar to execution of an if state-
ment that has no else clause. The <boolean expression> (which must
be inside parentheses) is evaluated and, if it is false, then control passes
to the statement after the while. If, on the other hand, the <boolean
expression> is true, then the <statement>, known as the body of the loop,
is executed once. Unlike the if statement, the while statement repeats
the process. The <boolean expression> is evaluated again and, if it is still
true, the <statement> is executed one more time. This is repeated un-
til the <boolean expression> is false, at which time control passes to the
statement after the while.
4.1. WHILE STATEMENTS 135
H ?
H
H Execute
Evaluate HH true - <statement>
HH <expression>
H once
H
H
false
?
Example 1
The following program reads integers and prints them along with their
squares. It continues to read and print until the user provides a value of
zero.
class PrintSquares
{
public static void main (String[] args)
{
System.out.println("Give an integer (zero to stop)");
int value = In.getInt();
while (value != 0)
{
System.out.println(value + " " + value*value);
System.out.println("Next integer (zero to stop)");
value = In.getInt();
}
}
}
136 CHAPTER 4. REPETITION
There are a number of points that we should note in the use of a while
statement in this program.
3. Because the statement(s) within the body of the loop will be executed
repeatedly as long as the condition that controls the loop is true, there
must be some action within the loop that will eventually cause the
condition to become false. In the example, this is accomplished by
reading a value of zero. We say that zero acts as a sentinel to halt
the loop.
4. If the boolean expression that controls the loop is false the first time
that the while statement is encountered, then the statement(s) inside
the loop will never be performed. Control will pass directly to the
statement following the while. In the example, this would occur if
the first value that a user entered were zero, the sentinel value.
5. If the expression that controls the loop is never changed to false, then
the program will get stuck in the while, executing it for as long as
the program is allowed to run. If this happens (and it will happen to
you, sooner or later), we say that the program is caught in an infinite
loop. The technique for stopping a program caught in an infinite loop
varies from system to system. There is usually some combination of
keys that will do the trick; your system supervisor should be able to
tell you what to do.
7. Once the loop has been exited, the boolean expression that controls
it must be false. (Otherwise we would never have left the loop.)
In many applications we want to find the total amount of some quantity.
Loops enable us to do this easily and efficiently.
Example 2
The following program uses a loop to sum a set of values and find their
average.
class AddMarks
{
public static void main (String[] args)
{
/* This program reads the marks obtained for a */
/* student on an exam and finds the average mark. */
/* It prints the average mark, rounded to the */
/* nearest integer. */
Exercises 4.1
1. What is the minimum number of times that the body of a while
statement can be executed?
2. What is the maximum number of times that the body of a while
statement can be executed?
3. What will be printed by this fragment?
int m = 10;
int n = 0;
while (m > n)
{
System.out.println(m + " " + n);
m--;
n += 2;
}
4. Write a program that reads a positive integer and then finds the
smallest power of two that is greater than or equal to the number
that was read. For example, if the program reads the value 25, it
should note that 32 = 25 is the smallest power of two greater than or
equal to 25.
5. Write a program that prompts the user for a sequence of integers,
using zero as a sentinel. The program should count the number of
times that consecutive values are equal. For example, if the input is
3 6 7 7 4 4 4 6 0
then the program should determine that there are three cases in which
consecutive values are equal.
4.2. DO STATEMENTS 139
4.2 do Statements
In the last section, we suggested that a while loop was Java’s way of
implementing commands of the form “as long as there are lumps in a sauce,
we should stir it”. If we rearrange this sentence a bit, we can rephrase it
as “stir the sauce as long as there are lumps in it”. The difference between
the two statements is subtle but it is often important. In the first case,
we repeatedly check for lumps first and then, if there are any, we stir. In
the second case we repeatedly stir and then check for lumps. For these two
kinds of repetitive instructions, Java has two kinds of loops. As we saw in
the last section, the while is appropriate for the first kind of situation. To
handle the second we have the do statement. The general form of the do
statement is:
do
<statement>
while (<boolean expression>);
To execute a do statement, Java first executes the <statement> and then
evaluates the <boolean expression> which must, as usual, be enclosed by
parentheses. If the expression is true, the process is repeated.
A flow chart for a do statement takes the following form.
?
Execute
<statement>
?
H
H
HH
Evaluate H true
HH <expression>
H
HH
false
?
140 CHAPTER 4. REPETITION
Example 1
This fragment forces a user to supply a value that lies in the range from
one to ten.
int value;
do
{
System.out.println("Give an integer from one to ten");
value = In.getInt();
}
while (value < 1 || value > 10);
Notice in the example that the condition that controls the loop is the
negation of what we want to happen. We want to get a value from one to
ten so we force the user to stay in the loop as long as the user is providing
values that are not in this interval.
Exercises 4.2
1. For what kind of looping situations is a do usually preferable to a
while?
2. What would be written by the program fragment shown below if it
were given the following input sequence?
-5
0
26
howMany = In.getInt();
if (howMany <= 0)
System.out.println("Give a positive value.");
}
while (howMany <= 0);
4. Write a program fragment that asks the user to supply a letter of the
alphabet, repeatedly rejecting responses until the user gives either an
upper case or a lower case letter of the alphabet.
5. Write a program that first forces the user to supply a positive integer
and then prints the number and the sum of its digits.
Java’s final loop statement is the for statement. Returning one last time to
our cooking analogy, we might have instructions for eliminating lumpiness
phrased in the form “stir mixture for 100 strokes”. Here we are not asked
to check on the state of the mixture before or after each stroke — we just
keep stirring until we have done one hundred repetitions. We can write
these instructions in a Java-like form as follows:
int count;
for (count = 0; count < 100; count++)
give sauce a stir;
To see exactly what this means, we can rewrite it using an equivalent
fragment that uses a while format, as follows:
int count = 0;
while (count < 100)
{
give sauce a stir;
count++;
}
By comparing the for and while forms, we can see that the first
expression inside the parentheses of the for is an initializer, the second
142 CHAPTER 4. REPETITION
Example 1
The following fragment finds the sum of the series 12 + 22 + · · · + 1002 . In
the fragment, the variables i and sum are both of type int.
sum = 0;
for (i = 1; i <= 100; i++)
sum += i*i;
The general form of Java’s for statement follows the form seen in the
example. The syntax of a for statement is given below.
for (<expression1 >; <expression2 >; <expression3 >)
<statement>
As we did with our cooking example, we can write a general for state-
ment using a while statement. The form of the for statement shown above
is exactly equivalent to the following fragment.
<expression1 >;
while (<expression2 >)
{
<statement>
<expression3 >;
}
Although the for and the while are equivalent, the format of the for often
makes the loop’s action much clearer to the reader. The structure of a for
statement and its relationship to a while statement may be made clearer
by studying its flow chart.
4.3. SIMPLE FOR STATEMENTS 143
?
Execute
<expression1 >;
?
HH
H
Evaluate HHtrue - Execute - Execute
HH<expression2 > <statement> <expression3 >;
H
H
H
false
?
Example 2
The program fragment shown here prints values of the function y = x3 − 3
for x taking values −6, −4, . . . , 6.
for (int x = -6; x <= 6; x += 2)
System.out.println(x + " " + (x*x*x - 3));
Exercises 4.3
1. What does each statement print?
1 × 2 × 3 × · · · × 20
5 x 1 = 5
5 x 2 = 10
5 x 3 = 15
5 x 4 = 20
5 x 5 = 25
Assume that the input is valid.
6. (a) Write a program that will print a table containing the integers
from one to forty along with their squares, square roots, and
reciprocals.
(b) Modify your program so that it inserts a blank line after every
fifth line of the table.
Example 1
The fragment shown below demonstrates the use of commas to build com-
plex expressions from simpler ones in for statements.
for (i = 0, j = 5; i < j; i++, j--)
System.out.println(i + " " + j);
The output from this fragment will be
0 5
1 4
2 3
The initialization here consists of the assignments i = 0 and j = 5 while
the modification after each repetition of the loop consists of both an incre-
ment of i and a decrement of j.
146 CHAPTER 4. REPETITION
Example 2
This fragment finds and prints the sum of the squares of the numbers from
one to ten.
for (i = 1, sum = 0; i <= 10; sum += i*i, i++)
;
System.out.println("Sum of squares is " + sum);
Here the body of the for consists only of an empty statement. We have
placed the empty statement’s semi-colon on a line by itself so that it is
clearly visible to a reader.
Example 3
The statement shown below produces a loop that will repeatedly print its
message as long as the program is allowed to run.
for ( ; true ; )
System.out.println("Help! I’m in an infinite loop!");
Notice that, although the first and third expressions are empty, the semi-
colons that are used as separators must still be included.
4.4. VARIATIONS ON FOR STATEMENTS 147
Exercises 4.4
1. Assuming that all variables are of type int, determine the output of
each fragment.
(a) product = 1;
for (int i = 10; i > 0; i--)
product *= i;
(b) max = i = 0;
j = 10;
for (; i < 10 ;)
if (i * j > max)
max = i++ * j--;
3. Write a for statement that has the form of the for statement in
Example 2 and that performs the indicated action.
1 2 3 19
+ + +···+
2 3 4 20
1 × 100 + 2 × 98 + 3 × 96 + · · · + 50 × 2
148 CHAPTER 4. REPETITION
Example 1
All three program fragments shown here have the same effect. They all
find the sum of the series
1 1 1 1
1+ + + +
2 3 4 5
In each part, the variable sum is of type double while i is of type int.
(a) sum = 0;
i = 1;
while (i <= 5)
{
sum += 1.0/i;
i++;
}
(b) sum = i = 0;
do
{
i++;
4.5. COMPARING LOOP STRUCTURES 149
sum += 1.0/i;
}
while (i < 5);
(c) for (sum = 0, i = 1; i <= 5; i++)
sum += 1.0/i;
The solution using a for statement is the shortest and clearest.
We have suggested that counted loops use for statements while condi-
tional loops use while or do statements. Sometimes, however, loops have
both of these elements. For these situations, the choice of structure is very
often a matter of personal preference.
Example 2
As part of a program to generate report cards, one segment is required
to read and total the marks. Most students take MAX_SUBJECTS subjects
and input for them consists of that many marks. Some students take fewer
subjects. Input for them consists of their marks followed by a sentinel value
of −1.
In this problem, the number of repetitions of the loop is sometimes
simply counted (for students taking the maximum number of subjects) but
sometimes conditional (for students with fewer subjects). The segment
shown below will give the correct values for totalMarks and numberOf-
Marks for input of either form. We have solved the problem with a do but
other solutions are possible.
totalMarks = numberOfMarks = 0;
do
{
System.out.println("Next mark please");
mark = In.getInt();
if (mark != -1)
{
totalMarks += mark;
numberOfMarks++;
}
}
while (mark != -1 && numberOfMarks < MAX_SUBJECTS);
150 CHAPTER 4. REPETITION
Exercises 4.5
1. Describe what the fragment does and then rewrite it using a for
statement.
(a) count = 1;
while (count < 10)
{
System.out.println(count+" "+Math.sqrt(count));
count++;
}
(b) i = 5;
do
{
i--;
System.out.println(Math.pow(i,3));
}
while (i > 0);
2. Describe what the fragment does and then rewrite it using a while
statement.
3. Describe what the fragment does and then rewrite it using a do state-
ment.
(b) for (i = 0; i == 0; )
{
System.out.println("Continue? (Y/N)");
response = In.getChar();
if (response == ’Y’ || response == ’N’)
i = 1;
}
4. State, with reasons, which loop structure you think would be most
appropriate for solving each problem. Do not actually solve the prob-
lem.
(a) Find the sum of the cubes of the numbers from 1 to 100.
(b) Repeatedly prompt a user for a password, rejecting any submis-
sions until the correct password has been provided.
(c) Read and sum positive integers until a sentinel value of −1 is
read.
(d) Determine the number of times that a positive integer can be
divided by two.
(e) Find the amount to which $1 will grow in ten years at a rate of
8%.
152 CHAPTER 4. REPETITION
In using a loop, the body can contain any valid Java statements, including
other loop statements. Thus we can have while, do, and for statements
within other while, do, and for statements. If a first loop contains a
second loop, we say that the second is nested within the first. The first
loop is referred to as the outer loop and the second as the inner loop.
To take a simple example, suppose that we want to print a triangular
pattern of asterisks, like the one shown below.
*
**
***
****
*****
The statement System.out.print("*"); will print one asterisk. To
print a complete line of asterisks, we can use a loop containing this state-
ment, with the loop followed by the statement System.out.println();
to end the line. To print a number of such lines, we can use another loop
containing the instructions for writing one line. The result is shown in the
next example.
Example 1
The following fragment prints a triangle of asterisks with the size of the
triangle determined by the value of numberOfRows.
for (row = 1; row <= numberOfRows; row++)
{
for (position = 1; position <= row; position++)
System.out.print("*");
System.out.println();
}
As we have already noted, loop nesting is not limited to for state-
ments. We can nest any form of loop inside any other form, as illustrated
in the next example.
4.6. NESTING LOOP STRUCTURES 153
Example 2
Suppose that we want to write a program that repeatedly prompts a user
to provide integer values (until the user provides a value of zero). For each
non-zero value read, the program prints the number and the sum of its
digits. We can begin to attack the problem by constructing code to handle
one value. Our strategy is to first eliminate any negative sign from the
number and then strip off the final digits, one by one, and add them up.
This continues until there are no more digits to strip off. A while loop
handles this nicely. In the fragment, n is the value whose digits are to be
added.
int absN = Math.abs(n);
int digitSum = 0;
while (absN > 0)
{
digitSum += absN % 10; // add last digit to digit sum
absN /= 10; // and strip it off the number
}
System.out.println("Sum of digits of "+n+" is "+digitSum);
Having solved the problem for one value of n, we can then embed this into
a loop that keeps prompting and reading until the user supplies a value of
zero. We have chosen to use a do statement for this loop. Here is the final
program.
class DigitAddition
{
public static void main (String[] args)
{
int n;
do
{
System.out.println("Give an integer (0 to stop)");
n = In.getInt();
if (n != 0)
{
int absN = Math.abs(n);
int digitSum = 0;
while (absN > 0)
{
154 CHAPTER 4. REPETITION
Exercises 4.6
1. Assuming that SIZE has the value 5, determine the output that will
be produced by the following fragment.
3. Write a program using nested loops that prints the following pattern.
1
12
123
1234
12345
4. Write a program using nested loops that prints the following pattern.
54321
4321
321
21
1
5. Three positive integers a, b, and c with a < b < c form a Pythagorean
triplet if a2 + b2 = c2 . For example, 3, 4, and 5 form a Pythagorean
triplet since 32 + 42 = 52 . Write a program that first prompts the
user for a positive integer and then finds and prints all Pythagorean
triplets whose largest member is less than or equal to that integer.
6. Write a program that will first prompt the user for the height and
width of a pattern and will then print a pattern that is the given
number of lines high and the given number of characters wide. The
pattern should contain stars and blanks similar to the pattern shown
in the flag of the United States. For example, if the user specified
a height of 5 and a width of 11, the program should produce the
following output. Assume that the user provides valid input.
* * * * * *
* * * * *
* * * * * *
* * * * *
* * * * * *
7. Write a program that first prompts the user (repeatedly, if neces-
sary) to supply a single digit (0, 1, 2, . . ., 9). The program should
then repeatedly prompt the user to supply integer values until the
user provides a value of zero which acts as a sentinel to terminate
the program. For each integer read (except for the final zero), the
program should count the number of occurrences of the digit read in
the first part of the program and should print this count.
156 CHAPTER 4. REPETITION
Avoiding Errors
1. In setting up a loop, take the time to choose an appropriate structure.
If a simple counted loop is required and you know how many times
the loop will be performed, use a for statement. If a conditional loop
is called for, you have to choose between a while or a do. To make
this decision, it is often helpful to use the following criteria.
(a) If it is easier to test the condition that controls the loop at the
start, you should probably use a while but if it is easier to test
at the end of the loop, use a do.
(b) If it is possible that the loop might be executed zero times, use
a while but if it is certain that it will be executed at least once,
a do might be more appropriate.
This will not print a table of squares of the integers from 1 to 100.
The body of the for statement contains only an empty statement
(terminated by the semi-colon on the first line). The println state-
ment is not part of the loop at all.
4.7. AVOIDING ERRORS AND DEBUGGING 157
5. Since while and if statements can have similar forms, it is not un-
common for beginning programmers to use the wrong one. A while
statement is a type of loop; an if statement is not.
Debugging
2. It is very easy to write a loop that performs one too many or one
too few iterations. The difference between failure and success of a
158 CHAPTER 4. REPETITION
The intention here is that the loop should execute ten times, for
x having the values 0.0, 0.1, 0.2, . . . , 0.9 and should stop after that.
Unfortunately, this does not occur. Java does not store floating point
values in decimal form. Consequently, values that have an exact
decimal form (like 0.1), are often stored as approximations by Java.
When x “should” have the value 1.0 (stopping the loop), it is actually
approximately equal to 0.999 999 999 999 999. Since this is less than
1.0, the loop is executed an eleventh time. The problem here could
be solved by using an integer counter for the loop, as follows.
Exercises 4.7
1. In the section on avoiding errors, we stated that the fragment
for (int i = 1; i < 100; i++);
System.out.println(i + " " + i*i);
would not print a table of squares of the integers from 1 to 100. What
would it print?
2. What value(s) of the variable response will stop the following loops?
(a) while (response <= ’a’ && response >= ’z’) ...
(b) while (response >= ’A’ || response <= ’E’) ...
3. (a) What is wrong with the following fragment?
4.7. AVOIDING ERRORS AND DEBUGGING 159
do
{
System.out.println("Enter transaction code");
char transCode = In.getChar();
System.out.println("Code entered: " + transCode
+ "\nIs this correct? (Y/N)");
char response = In.getChar();
}
while (response != ’Y’ || response != ’N’);
(b) Modify the fragment so that it behaves in a more appropriate
way.
4. How many times will the following loop be executed? Justify your
answer.
int n = 40;
while (n > 0);
{
System.out.println(n);
n /= 2;
}
value++;
n /= 2;
}
(c) n = In.getInt();
value = 0;
do
{
value += n % 10;
n /= 10;
}
while (n > 0);
3. A prime number is a positive integer that has exactly two distinct
divisors — one and the number itself. For example, 17 is a prime
number because it is divisible only by 1 and 17. Write a program
that reads an integer and then prints a message stating whether or
not it is a prime number.
4. The number 153 has the property that it is equal to the sum of the
cubes of its digits: 13 + 53 + 33 = 153. Write a program that will find
all the three-digit natural numbers that have this property.
5. Write a program that prompts the user for a natural number and,
once one has been supplied, finds and prints all of its exact divisors.
6. The number 6 is said to be a perfect number because it is equal to
the sum of all its exact divisors (other than itself).
6 = 1+2+3
Write a program that finds and prints the three smallest perfect num-
bers.
7. (a) Write a program that could be used to play a simple game. The
program should first ask one user to supply a positive integer less
than 1000. Once this user has provided an appropriate value,
the program should than ask a second user to guess the number
that was given by the first user. After each incorrect guess, the
program should tell this user whether the guess was too low or
too high and ask for another guess. This should continue until
the second user has found the correct number. The program
should then print the number of guesses that the second user
needed to determine the number.
162 CHAPTER 4. REPETITION
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30
11. Write a program that computes the amount to which periodic de-
posits of $1 will grow at a given rate of interest and for a given length
4.8. REVIEW EXERCISES 4 163
n! = n × (n − 1) × (n − 2) × · · · × 3 × 2 × 1
13. Write a program that reads a positive integer and then checks to see
whether or not the integer is divisible by 11. For the divisibility test,
use the following algorithm, based on one given by the mathematician
Charles S. Dodgson (also known as Lewis Carroll).
• As long as the number has more than one digit, shorten it by
deleting the units digit and subtracting this digit from the re-
sulting number.
• The original number is divisible by 11 if and only if the final
number is equal to zero.
You may assume that the input is valid and that it can be stored as a
long value. Output from the program should consist of the original
number followed by any shortened numbers obtained by applying the
algorithm followed, finally, by a message stating whether or not the
original number was divisible by 11.
164 CHAPTER 4. REPETITION
Projects
14. In the nine-digit Social Insurance Number (SIN) given to each person
having a job or filing an income tax return in Canada, the ninth digit
is a check digit that is used to test the validity of the other digits in
the SIN. The ninth digit is determined by the following procedure.
(a) Double the 2nd, 4th, 6th, and 8th digits.
(b) Add the digits of the numbers found in step (a).
(c) Add the 1st, 3rd, 5th, and 7th digits.
(d) Add the numbers found in steps (b) and (c).
(e) Subtract the units digit of the result of step (d) from 10 and
note the units digit of the result. For the SIN to be valid, its
ninth digit must have this value.
Write a program that repeatedly reads nine-digit numbers and de-
termines whether or not each number is a valid SIN. The program
should stop when it reads the value 999999999.
15. The diagram shows a unit square in the Cartesian plane and a quarter
circle of radius one, centred at the origin.
y
1
x
O 1
4.8. REVIEW EXERCISES 4 165
(a) Show that, if a point is chosen at random within the square, the
probability that it will also be inside the quarter circle is π/4.
(b) Write a program that repeatedly generates the coordinates of a
random point within the square and determines whether or not
the point also lies inside the quarter circle. The program should
repeat the process 10 000 times. After every 1 000 repetitions,
it should use the results to determine and print an estimate of
the value of π (based on all repetitions up to that point).
16. Assuming that there are 365 days in a year and that the probability
that a person will be born on a given day is 1/365, we can calculate
the probability that, in a group of five people chosen at random, at
least two will have the same birthday, as follows:
We start by calculating the probability that all five have different
birthdays. This will be
365 364 363 362 361
× × × ×
365 365 365 365 365
The reasoning here is that the first person could be born on any of
365 days; the second person, to have a different birthday, could only
be born on 364 out of 365 days; the third person, to have a birthday
different from either of the first two, could only be born on 363 out of
365 days; and so on to the fifth person. Now, to find the probability
that at least two people in a randomly chosen group of five have the
same birthday, we simply subtract the above expression from 1 to
obtain
365 364 363 362 361 .
1− × × × × = 0.027
365 365 365 365 365
Write a program that produces a table showing the minimum number
of people that would be required so that the probability of at least
two people in a randomly chosen group having the same birthday is
at least 0.1, 0.2, . . . , 0.9, 1.0
17. Write a program that will determine the roots of equations of the
form ax2 + bx + c = 0. The program should repeatedly prompt the
user for values of a, b, and c. For each set of values, the program
should solve the corresponding equation, if it has a solution, or print
an appropriate message, if it has no solution. The program should
be able to cope with coefficients that produce either real or complex
166 CHAPTER 4. REPETITION
18. Write a program that will perform the four basic arithmetic opera-
tions (addition, subtraction, multiplication, and division) on pairs of
fractions, producing answers in standard fractional form. Specifically,
your program should repeatedly perform the following actions.
• Read a character representing an operation. This will be one of
the characters +, -, *, /, or $. The $ signals the end of the data;
reading it should cause the program to terminate.
• Read four integers representing the numerator and denominator
of a first fraction followed by the numerator and denominator of
a second fraction.
• Perform the indicated operation and print the results in the
appropriate form as illustrated in the examples that follow.
4.8. REVIEW EXERCISES 4 167
In the examples, the input is shown on one line. The program, how-
ever, should read the input items one at a time, with each one on a
separate line.
(a) Input: + 1 3 -19 -8
Output: 1/3 + -19/-8 = 2 and 17/24
(b) Input: / 2 -5 3 -7
Output: 2/-5 / 3/-7 = 14/15
(c) Input: - 5 2 1 2
Output: 5/2 - 1/2 = 2
Notice that improper fractions should be written as mixed numbers,
as in (a), and integer results should be written as such, as in (c). It is
not, however, necessary to reduce fractions to lowest terms. The pro-
gram should assume that a user will provide integers when required
but it should not assume that the integers will produce valid results.
For example, bad data such as
+ 3 5 4 0
should be detected by the program. In such cases, the program should
write an error message and then continue to process the next set of
data.
19. The Russian Peasant Algorithm is a process for determining the prod-
uct of two positive integers using only multiplication by two, division
by two, and addition. The Russian Peasant Algorithm can be stated
as follows:
• Write each number (the multiplier and the multiplicand) at the
head of a column.
• Double the number in the first column, and halve the number
in the second column.
• If the number in the second column is odd, ignore the remainder
when dividing.
• If the number in the second column is even, cross out that entire
row after dividing.
• Keep doubling, halving, and crossing out until the number in
the second column is 1.
• Add up the remaining numbers in the first column.
• The total is the product of your original numbers.
168 CHAPTER 4. REPETITION
/7
5 / /6
8 /
114 43
228 21
/5
4 /6/ /0
1 /
912 5
/8
1 /2/4/ /
2
3648 1
4902
Write a program that prompts the user for a multiplier and a multi-
plicand and then uses the Russian Peasant Algorithm to determine
the product. Your program must also show the intermediate steps
the algorithm takes by printing out any row where an addition is re-
quired in the first column. Your program should continue prompting
the user for input until two zeros are entered. This should terminate
the program. If a user provides negative values for either the mul-
tiplier or the multiplicand, these should be rejected. If one value is
zero and the other is positive, the program should give the correct
result without using the algorithm. Using the example above, your
program should function as shown in the following sample:
Multiplier: 57
Multiplicand: 86
Calculating product:
114 43
228 21
912 5
3648 1
Product: 4902
Multiplier: 48
Multiplicand: -36
Values must not be negative
Multiplier: 27
Multiplicand: 0
Product: 0
Multiplier: 0
Multiplicand: 0
Chapter 5
Methods
5.1 Basics
Example 1
The following code defines a method that can be used to print a heading at
the top of a page. The escape sequence \f in the first println statement
causes a “form feed” — a jump to the top of a new page.
The method definition shown in Example 1 looks very much like that
of a main method except that the word main has been replaced by the
method’s identifier, printHeading, and the parentheses that follow the
method’s identifier are empty.
Once we have defined a method, the simplest way to use it is to place
its code in the same class as the program’s main method. The definition
of the new method can be placed either before or after the main method.
If an application program contains a number of methods, then exactly one
of them must be called main. When we run such a program, Java seeks
out the main method and starts execution of the program at the beginning
of this method. Although the main method can be placed anywhere, it is
customary to place it either first or last.
When we use a method, we say that we call it or invoke it. If, for
example, we have placed the definition of our printHeading method in a
class along with a main method, then we could call printHeading from
within main by writing the statement
printHeading();
We must write the parentheses because that is the way that we tell Java
that printHeading is a method, not a variable.
172 CHAPTER 5. METHODS
Example 2
The following program skeleton shows how the method printHeading
might appear and be called in a program.
class Sample
{
public static void printHeading ()
{
// method definition as shown in Example 1
.
.
}
Despite the fact that the definition of printHeading precedes that of main,
execution of the program starts with the first statement in main. When-
ever the statement printHeading(); is encountered, control passes to that
method, its statements are executed, and then control passes back to the
statement immediately following the call to the method.
Like the main method, methods that we write will, unless directed
otherwise, execute their statements until they come to the method’s final
5.1. BASICS 173
Example 3
The method printRoot will read a value and then find and print its square
root if and only if the value is non-negative.
public static void printRoot ()
{
System.out.println("Please give a non-negative value");
double x = In.getDouble();
if (x < 0)
return;
else
System.out.println("Square root is " + Math.sqrt(x));
}
Exercises 5.1
1. Explain the difference between a method definition and a method
invocation.
2. In the following program, the executable statements are numbered.
Use these numbers to indicate the order in which the statements are
executed.
class Song
{
public static void printChorus ()
{
System.out.println(); //1
System.out.println("Ee-igh, ee-igh, oh!"); //2
System.out.println(); //3
174 CHAPTER 5. METHODS
5.2 Parameters
The methods that we looked at in the previous section did exactly the same
thing every time that we called them. To make methods more flexible,
it is often desirable to be able to give them different values to start their
calculations. To see how to do this in Java, let us look at a simple example.
Example 1
The following method will print a line containing a given character (speci-
fied by c) repeated a given number of times (specified by n). The method
will print a blank line if the value of n is less than one.
public static void printRow (char c, int n)
{
for (int i = 1; i <= n; i++)
5.2. PARAMETERS 175
System.out.print(c);
System.out.println();
}
Example 2
Consider the statements
int m = 6;
printRow((char)(’A’+3),m+2);
The call to printRow would cause the following sequence of actions to take
place.
1. Using the value of the variable m at the point of the call, the argu-
ments of printRow would be evaluated.
Point of Call
m
6 (char)(’A’+3) ⇒ ’D’
m+2 ⇒ 6+2 ⇒ 8
1 In other texts you may see parameters called formal parameters and arguments
called actual parameters. We prefer the shorter names.
176 CHAPTER 5. METHODS
DDDDDDDD
Be sure that you understand the process that Java uses in calls in-
volving parameters — an argument is evaluated and then this value is
automatically assigned to the corresponding parameter. This process is
known as call by value. Other programming languages use other parameter
passing mechanisms but Java uses call by value exclusively. One of the
consequences of this is that the communication between arguments and
parameters is in one direction only — from the arguments to the parame-
ters. The parameter is given a copy of the value of the argument, not the
argument itself.
5.2. PARAMETERS 177
Example 3
Consider the following method outline. (The dots indicate omitted mate-
rial.)
public static void sample (int n)
{
...
n++;
...
}
Suppose now that we were to call this method by writing
int n = 3;
sample(n);
This would produce the following sequence of events.
1. The value of the argument of sample is evaluated.
Point of Call
n
3 n ⇒ 3
3. The method sample is then executed, changing the value of the pa-
rameter n to 4.
Method sample
n
4
178 CHAPTER 5. METHODS
Example 4
Consider the following method heading:
public static void doThis (int n, double x)
The arguments for this method would normally be an int followed by a
double but it would also be valid to call the method with a char and a
float or a byte and an int to name only some of the possibilities.
Exercises 5.2
1. A student wrote the following method to exchange the values of two
int variables.
i = 7;
j = 3;
swap(i,j);
System.out.println("i = " + i + " and j = " + j);
Methods can be divided into two groups, often known as commands and
queries. The methods that we have been looking at so far are all examples
of commands; they are called to perform some task and, once that task is
complete, they simply return control to the point at which they were called.
Queries, on the other hand, are used to calculate some value which is then
returned to the point at which the method was called. In many program-
ming languages, there are two kinds of methods, often known as procedures
(to implement commands) and functions (to implement queries). Although
Java does not use either of the terms procedure or function, methods of
both types can be created. To see how to create queries, let us consider an
example.
180 CHAPTER 5. METHODS
Example 1
The following method calculates values of the function whose defining equa-
tion is 3
x if x < 0
f(x) =
x2 if x ≥ 0
There are two new features to note about this method definition.
1. The word void in the header has been replaced by the word double.
This is the type of value that the method will be returning.
The return statement need not be at the end of the method and there
may be more than one such statement. In Example 1 we could have written
the method definition in the form
We can visualize the process of using a query with diagrams like those
we used for commands. The next example illustrates a call to the method
f, using the version of f shown in Example 1.
Example 2
Suppose that we make a call to the method f by writing
double a = 3;
double b = f(a-1);
This would produce the following sequence of events.
Example 3
Assuming that f has been defined as shown in Example 1 and that all
variables shown are of type double, then each of the following calls is
valid.
(a) fVal = f(2*z);
(b) total = p + q + f(r);
(c) smaller = Math.min(f(s),f(t));
(d) System.out.println("The result is " + f(a+b));
The rules for parameter passing with methods that return values are
identical to those for methods that do not do so. The number of argu-
ments must be equal to the number of parameters and the types must be
appropriate.
Exercises 5.3
1. Study this method and then answer the questions that follow it.
(a) System.out.println(f(-7));
(b) double x = f(-7);
(c) double x = System.out.println(f(-7));
(d) double x = -7; f(x);
4. Write a method largest that returns the value of the largest of its
three double parameters.
5. Write a method gcd that returns the value of the greatest common
divisor of its two int parameters.
various numbers. We have seen this already in the use of the predefined
method System.out.println that performs correctly whatever type of ar-
gument we give it. This is an example of method overloading.
One of the most common forms of method overloading is seen in meth-
ods that have a different number of parameters. As an example, suppose
that we want to use a method called rollDie that prints the result of
rolling a die (the singular of dice). We might have a variety of methods to
perform this task, depending on the number of times we want to roll the
die and its shape.
Example 1
Methods to simulate the rolling of a die could take a number of overloaded
forms.
• One method might simply simulate the roll of one standard cubic die
with six faces.
public static void rollDie ()
{
System.out.println((int)(6*Math.random()+1));
}
We could call this method by writing
rollDie();
to print the result of rolling an ordinary die.
When a call is made to a method, the Java compiler does not simply
try to find a method with an identifier that matches the identifier in the
call. Instead, it performs a process called signature matching. We say that
a method signature is determined by the method’s identifier, the number of
parameters, and the type(s) of the parameter(s) specified in the method’s
header.
In making a call, the compiler first tries to make an exact signature
match. If it cannot do so, even though the number of arguments matches
the number of parameters, it tries to see if it can make a widening conver-
sion of the type of any of the arguments so that the resulting type matches
the type of the corresponding parameter. If the method is overloaded, the
version with the correct number of parameters that requires the minimal
amount of widening is considered to be the best match and this is the one
that will be called. If no match can be produced in this way, the call is
invalid.
Example 2
Suppose that a class contains method definitions with the following headers:
public static void doThat (int m, int n)
and
public static void doThat (double x, int m)
(d) The call doThat(3L,4); would invoke the second method; although
there is no exact match for either version, widening 3L to the double
value 3.0 produces a match for the second one.
Example 3
Suppose that a class contains method definitions with the following headers:
public static void doOther (int n, double x)
and
public static void doOther (double x, int n)
In this situation, the call doOther(2,3); would be ambiguous since both
versions of doOther require one conversion from an int to a double. The
call would produce an error.
Exercises 5.4
1. Find an example of method overloading in Java’s Math class.
2. Suppose that two method definitions have these headers:
A: public static void foo (int m, long n)
B: public static void foo (int i, int j)
For each of the following calls, state, with reasons, whether or not the
call is valid and, if it is valid, which version of foo would be called.
(a) foo(8,4); (b) foo(2L,3L);
(c) foo(’A’,5); (d) foo(4,0.5);
(e) foo(7,6L); (f) foo(’x’,’y’);
Example 1
The fragment
if (13 <= age && age <= 19)
could be replaced by
if (isTeen(age))
where isTeen is a method whose definition could be
public static boolean isTeen (int n)
{
if (n >= 13 && n <= 19)
return true;
else
return false;
}
Exercises 5.5
1. Write boolean methods that could be used to make each fragment
more readable.
(a) while (n % 2 == 1)
.
.
(b) if (’0’ <= c && c <= ’9’)
.
.
(c) do
.
.
while (nextChar == ’.’ || nextChar == ’,’ ||
nextChar == ’?’ || nextChar == ’:’ ||
nextChar == ’;’ || nextChar == ’!’);
3. Write a boolean method isLetter that returns true if and only if its
single char parameter is a letter of the alphabet (either upper case
or lower case).
4. Write a boolean method isPrime that returns true if and only if its
int parameter is a prime number.
5. Write two boolean methods, both called isPass, that could be used
with the following fragment.
}
else // assume numerical grade
{
int mark = In.getInt();
if (isPass(mark))
System.out.println("Pass");
}
The methods should return true if and only if the mark is a pass (at
least ’D’ for a letter grade and at least 50 for a numerical grade).
We have said previously that variables can usually be used at any point
after they have been declared. The one exception to this that we have seen
is that a variable declared in the initializer part of a for statement can
only be used within the for statement. The range in which an identifier
can be recognized is known as its scope. Now that we have programs with
more than one method, we must modify our idea of scope.
First, we can make our previous ideas of scope within any method
(including the main method) more precise. A block is a section of code
enclosed by brace brackets. The scope of a variable is the part of the code
from the declaration of the variable down to the brace bracket that closes
the block in which the variable was declared.
Example 1
In the method sketched here, the comments indicate the scope of the vari-
ables i, j, and k. (The dots indicate code that we have omitted.)
public static void sample ()
{
int i = 1; // scope of i starts here
... // cannot use j or k here
for (int j = 0; ...) // scope of j starts here
{
int k = 1; // scope of k starts here
190 CHAPTER 5. METHODS
sample
int i
int j
int k
Example 2
Consider the following outline of a method:
sample
double a
int i
double i
Note that, if the double variable i had been declared at the beginning of
the method, this would have produced a compilation error because, in that
case, the int variable i would be nested inside the scope of the double
version.
192 CHAPTER 5. METHODS
these circumstances, we can omit the public modifier for methods other
than main.
Exercises 5.6
1. In the method outline shown here, state which variables can be used
at each numbered line.
2. What is a block?
Example 1
A pseudo-code solution to the problem of testing the validity of Goldbach’s
Conjecture might take the following form.
get first value from user
while (value != 0)
2A prime number is a positive integer that has exactly two distinct factors: one and
the number itself.
5.7. PROGRAMMING WITH METHODS 195
{
test Goldbach’s Conjecture on value
get next value from user
}
The pseudo-code provides the basis for a main method in our solution
to the problem. Some lines of the pseudo-code can be converted directly
into single lines in the Java language; others require a number of statements.
For the latter type, we can write methods. Thinking this way, we can write
the main method.
Example 2
The pseudo-code of the previous example can be converted to Java as the
following main method.
Having designed an overall strategy for the solution, we can now focus
on the parts that have not yet been analyzed in detail. Here there are
two parts that need expansion: the method nextNumber that reads values
and the method testGoldbach that tests the conjecture for a given even
number.
The method nextNumber should prompt a user for a value and read
a value, rejecting invalid input from the user. The next example shows a
possible implementation of this method.
196 CHAPTER 5. METHODS
Example 3
The method nextNumber forces a user to supply a non-negative even num-
ber as input. Since the method is only going to be used within this program,
we have made its visibility private so that it cannot be called from outside
the class in which it is defined.
private static int nextNumber ()
{
int response;
do
{
System.out.println("Give an even integer > 2 "
+ "(0 to stop)");
response = In.getInt();
}
while (response<0 || response%2!=0 || response==2);
return response;
}
The method testGoldbach should test the conjecture for a given even
number, n, and print either two primes whose sum is n or a message that the
conjecture is not true. To see how we might solve the problem in general,
it might be useful first to see how we might proceed with a particular case.
We do this in the next example.
Example 4
To determine whether or not the value 12 satisfies Goldbach’s Conjecture,
we must try to find two primes that sum to 12. To do so, we can break 12
into two parts in various ways, using increasingly larger primes as the first
part of the number and testing the differences between these primes and
12 to see if they are also primes. The smallest prime is two, so we start
there. The table summarizes our search.
Prime Difference Result of Test
2 10 10 is not prime — continue
3 9 9 is not prime — continue
5 7 7 is prime — conjecture verified for 12
5.7. PROGRAMMING WITH METHODS 197
If we had not had success on the third try, the next pair that we would
have tried would have been 7 and 5 but there would be no need to do so
because we would have already examined 5 and 7. Thus, once our first
part has reached half way to the value being examined, we can quit. If no
pair of primes has been found at this point, Goldbach’s Conjecture must
be false.
Example 5
A pseudo-code solution to the problem of testing Goldbach’s Conjecture
for a positive even integer n could take the following form.
firstPart = 2
while (conjecture not verified or rejected)
{
secondPart = n - firstPart
if (secondPart < firstPart)
conjecture is false: print message
else if (secondPart is prime)
we have verified the conjecture: print parts
else
firstPart = next prime after current firstPart
}
Number theory tells us that there are infinitely many primes so that we
can always find a next prime after the current first part. This ensures that
the assignment in the last line of code will always be successful.
198 CHAPTER 5. METHODS
Example 6
The following Java method tests a positive even integer to see if it satisfies
Goldbach’s Conjecture. The loop is controlled by a boolean flag done that
is initially set to false. It is made true if the conjecture is either verified
or refuted. The method assumes that n is a positive even integer greater
than two, a condition that is assured by the method nextNumber.
two other methods: isPrime that tests a number to see if it is prime and
primeAfter that returns the next prime following a given value. We have
asked you to supply a definition for the method isPrime in the exercises.
The next example contains a definition of the method primeAfter.
Example 7
The method primeAfter shown here returns the next prime following its
argument n. It assumes that n is a prime number. If n = 2, the next
prime is 3 but otherwise n must be odd and only succeeding odd values are
examined.
private static int primeAfter (int n)
{
int value;
if (n == 2)
value = 3;
else
{
value = n + 2;
while (!isPrime(value))
value += 2;
}
return value;
}
Example 8
The class GoldBach contains an almost complete solution to the problem
posed at the start of this section: to attempt to verify Goldbach’s Conjec-
ture for values supplied by a user. One method has been left to be supplied
by the reader.
/**
* This program repeatedly prompts a user for even integers
* greater than 2, forcing the user to supply valid values.
* The user can terminate the program by supplying a value of
* zero. For each valid input value, the program determines
* whether or not Goldbach’s Conjecture holds for that value
* and prints an appropriate message.
*
* Goldbach’s Conjecture states that any even integer greater
* than 2 can be written as the sum of two primes.
*
* @author Adrienne Geoffrey
* @version 1.0 July, 2002
*/
class Goldbach
{
/**
* Repeatedly get numbers and test each one to see
* if Goldbach’s Conjecture is correct for that value.
* The sentinel value zero causes the program to stop.
*/
public static void main (String[] args)
{
int value = nextNumber();
while (value != 0)
{
testGoldbach(value);
value = nextNumber();
}
}
/**
* Prompt a user for a non-negative even integer other
5.7. PROGRAMMING WITH METHODS 201
/**
* Test Goldbach’s Conjecture for a given integer and print
* an appropriate message based on the results of the test.
*
* @param n an integer assumed to be even and > 2
*/
private static void testGoldbach (int n)
{
int firstPart = 2;
boolean done = false;
while (!done)
{
int secondPart = n - firstPart;
if (secondPart < firstPart)
{
System.out.println("Goldbach’s Conjecture is "
+ "false - it fails for " + n);
done = true;
}
else if (isPrime(secondPart))
{
System.out.println("Goldbach’s Conjecture is true "
202 CHAPTER 5. METHODS
/**
* Find next prime number (in increasing order) after n.
*
* @param n an integer assumed to be a prime number
* @return an int - the next prime after n
*/
private static int primeAfter (int n)
{
int value;
if (n == 2)
value = 3;
else
{
value = n + 2;
while (!isPrime(value))
value += 2;
}
return value;
}
}
5.8. AVOIDING ERRORS AND DEBUGGING 203
Exercises 5.7
1. Find two primes whose sum is equal to the given number.
(a) 4 (b) 28
(c) 38 (d) 54
Avoiding Errors
1. When naming methods, you should, as usual, choose descriptive iden-
tifiers. In addition, there are some naming conventions for methods
that, if followed, should make your programs clearer and consequently
less prone to errors. Since queries return values, it is common to name
them with identifiers that indicate the value returned (total, sqrt,
finalValue, and so on). Commands, on the other hand, do not re-
turn values; they perform some action. It is common to name them
with identifiers that indicate the action that they perform (println,
swap, rollDie, and so on).
204 CHAPTER 5. METHODS
if (a == b)
return x;
else
return y;
Debugging
set to true when debugging and false otherwise. Then replace the
body of a method with something like the following.
if (DEBUG)
// action to be taken if debugging
else
// regular code of method
Exercises 5.8
1. What is wrong with each method?
2. Add statements to the method range that could be used to trace its
actions.
206 CHAPTER 5. METHODS
int e;
... // 4
}
... // 5
{
int f;
... // 6
}
int g;
... // 7
}
5. (a) Write a definition of a method norm that has three double pa-
rameters, x, y, and z. The method should return, as a double
1
value, the value of the expression x2 + y2 + z 2 2
(b) For variables a, b, and c, write statements that use norm to as-
sign to the indicated variables the values of each of the following
expressions:
1 1
i. p 1/ a2 + b2 + c2 2 ii. q a4 + b4 + c4 2
2 1 2 12 2 12 2 12
iii. r 4a + 9b2 + 25c2 2 iv. s 3a 12b 27c
Mark Grade
0 - 49 F
50 - 59 D
60 - 69 C
70 - 79 B
80 - 100 A
Others X
10. (a) Write a definition of a method leastFactor that has one int
parameter, n. If n > 1, the method should return the value of
the smallest prime factor of n; otherwise, it should return the
value zero.
(b) Write a program that uses leastFactor to find and print all
the prime factors of numbers read as input. For example, given
input of 12, the program should note that the prime factors are
2, 2, and 3. The program should be interactive, prompting the
user for values and processing them until a value less than one
is supplied by the user.
Projects
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
0
0
1 1
0 1
2 2 2
0 1 2
3 3 3 3
0 1 2 3
4 4 4 4 4
0 1 2 3 4
n
The value of r
can be calculated using the formula:
n n!
=
r r!(n − r)!
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
Your program should be able to print up to twenty rows of Pascal’s
triangle. (Larger triangles contain entries that are more than five
digits long; they will not fit in the format specified for the problem.) If
a user specifies a number of rows greater than twenty or less than zero,
the program should reject the value gracefully. For large triangles,
your output device may not be able to print all the characters in
some rows on a single line. In such cases, the output of one row will
automatically wrap around to the next line. Do not worry about this
behaviour.
Chapter 6
This class is very different from the ones that we have seen up to this
point in our study of Java. It does not have a main method; in fact, it
has no methods at all! It simply defines what something called a Fraction
should look like: something that has two int parts that we have called a
num and a den. We call these parts fields of the class. A field is declared
outside a method while a local variable is declared inside a method.1 We
can illustrate the structure of the class with a diagram, as follows:
Fraction
num
den
1 We should warn you that, although we think that this distinction between fields and
local variables is a useful one, not everybody uses these terms.
6.1. CREATING OBJECTS 215
Fraction
- num 0
den 0
Notice in the preceding diagram that the num and den fields are both
shown with the value zero. Although Java does not initialize local variables
when we declare them, it does initialize all numeric fields to the value zero
when we create an object containing those fields. We can (and usually will)
combine the process of declaring and creating a new Fraction object f by
writing
Fraction f = new Fraction();
216 CHAPTER 6. CLASSES AND OBJECTS
2
If we wanted our object to represent the fraction 3
rather than 00 , we
could now make the following assignment statements:
f.num = 2;
f.den = 3;
f’s num = 2;
f’s den = 3;
Fraction
- num 2
den 3
Using the same template, we can create as many objects of that type
as we wish.
Example 1
3
To create two fractions, f1 and f2, representing the fractions 4 and 56 , we
could write:
f1 f2
Fraction Fraction
- num 3 - num 5
den 4 den 6
Example 2
Suppose that f1 and f2 have been created as shown in the previous exam-
ple. If we were to write
f1.num--;
f1.den = f2.den + 3;
3−1 2
then f1 would represent the fraction = and f2 would be un-
6+3 9
changed.
that it has no value. We show the null reference of f2 with the symbol
used to indicate a ground in a diagram of an electrical circuit.
f1 f2
.
As we have already seen with strings, objects are very different from
primitive types so, when we attempt to apply operations to objects, we
must be very careful of what we are doing. Otherwise, the results can be
something quite different from what we want. The next example examines
the meaning of assignment with objects.
Example 3
Suppose that we have created an object f of our Fraction class by writing
Fraction f = new Fraction();
f.num = 5;
f.den = 8;
to obtain the object shown in the next diagram.
f
Fraction
- num 5
den 8
If we now write
Fraction g = f;
the result is the usual one produced by an assignment: a value in one
memory location is copied into another memory location. Here, however,
the copying is done from the reference f to the reference g, producing the
result shown in the next diagram where f and g, since they have the same
value, both refer to the same object.
6.1. CREATING OBJECTS 219
f g
Fraction
- num 5
den 8
Exercises 6.1
1. What is the essential difference between a local variable and a field?
Fraction p;
p.num = 7;
p.den = 8;
4. Draw diagrams like those shown in the text to illustrate the results
of the given fragment.
Fraction p, q, r;
p = new Fraction();
q = new Fraction();
r = q;
p.num = p.den = 2;
220 CHAPTER 6. CLASSES AND OBJECTS
q.num = 2*p.den;
p.den++;
--p.num;
r.den = p.num + 2;
class Circle
{
double x; // x-coordinate of centre
double y; // y-coordinate of centre
double r; // radius
}
c1
Circle
x 5
- y 2
r 3
Using this class, write a Java program that performs the following
actions.
6.2. INSTANCE METHODS 221
Objects are more than just collections of data; they can also have function-
ality, implemented through methods. The methods associated with objects
can be grouped into two primary categories. In this section, we examine
the more commonly used type — instance methods. We have already en-
countered instance methods in the String class where the methods equals
and compareTo are of this type. Here we will examine the structure of such
methods and learn how to create our own.
As an example of an instance method, suppose that we want to extend
our Fraction class with a method that returns, as a double value, the
magnitude or size of a fraction. We define the size of a fraction ab as ab .
To implement such a method, we could add the method shown in the next
example to our Fraction class.
222 CHAPTER 6. CLASSES AND OBJECTS
Example 1
The following instance method will return the magnitude of a Fraction
object.
public double size ()
{
return Math.abs((double)num/den);
}
If, in our main method, we had created and defined a Fraction object
f, then we could determine its size, s, by writing
double s = f.size();
In line with our suggestion in Section 6.1, it may be helpful if you read this
statement as
s = f’s size
Example 2
The following method uses the size method to compare two objects of
type Fraction. It returns a reference to a Fraction object, the larger of
the two (or the first if they have the same size).
If f, g, and h are all of type Fraction, then we could use the larger
method by writing a statement such as
h = f.larger(g);
This would assign to h a reference to the larger when we compare f to g
(or to f if f and g are the same size).
Suppose that, before this call, we had the following situation:
224 CHAPTER 6. CLASSES AND OBJECTS
f g h
.
Fraction Fraction
- num 1 - num 2
den 2 den 3
2
Then, after the call, h would also refer to g, because 3 > 12 . Pictorially, we
would have:
f g h
Fraction Fraction
- num 1 - num 2
den 2 den 3
Note the use of the reserved word this in the example. As we have said
previously, this refers to the implicit object associated with an instance
method. In the call f.larger(g), this would refer to f while the explicit
parameter, other, would refer to g. In the definition of the larger method,
the first line of the if statement could have been written more simply as
if (size() >= other.size())
Normally a call to an instance method such as size requires an object but,
if we omit an object reference, Java assumes that we intended to use this,
the current implicit object reference.
As with any other methods, instance methods can take the form of
commands that do not return values. The next example illustrates this.
Example 3
The method timesEquals has the same effect (for Fraction objects) that
the *= operator has for primitive numeric types. It assigns to its implicit
Fraction parameter the product of itself with its explicit Fraction pa-
rameter.
6.2. INSTANCE METHODS 225
Example 4
The method times, when called by the statement
Fraction f = g.times(h);
where g and h are both Fraction objects, returns a new Fraction object
equal to the product of g and h while leaving both g and h unchanged.
In the example, the variable result is local to the method but this does
not mean that the Fraction object to which result refers is lost when
execution of the method terminates. The statement
Fraction f = g.times(h);
returns a reference to the newly created object. This reference is then
assigned to f so that f now refers to the new object, as required.
Once again, we can illustrate the effect of this method with diagrams.
Suppose that, before the call to times, we had the following:
g h
Fraction Fraction
- num 2 - num 1
den 5 den 4
g h
Fraction Fraction
- num 2 - num 1
den 5 den 4
Fraction
- num 2
den 20
Exercises 6.2
1. Suppose that p, q, and r are all objects of type Fraction. What
fraction would r represent after the statement
r = p.larger(q);
is executed given that larger is the method in Example 2 and
1
(a) p represents 3
and q represents 45
7 −9
(b) p represents −5 and q represents −7
5 −25
(c) p represents 6 and q represents −30
−9
(d) p represents −12
and q represents −34
5 13
(e) p represents 8
and q represents 20
2. Complete the definitions of the following instance methods for the
Fraction class.
(a) public void plusEquals (Fraction other)
The method should have the effect (for Fraction objects) that
228 CHAPTER 6. CLASSES AND OBJECTS
class Complex
{
double re:
double im;
}
(a) Write an instance method modulus for this class that could be
called by a statement like
double size = z.modulus();
where z is of type Complex. If z represented the value a + ib,
then
√ the call would set the variable size to the value of |z| =
a2 + b 2 .
(b) Write an instance method called scale for the class Complex
that could be called by a statement like
z.scale(x);
where z is of type Complex and x is a double value. If, before
the call, z represented the value a + ib, then, after the call, it
should represent the value x(a + ib).
4. Assuming that z1, z2, and z3 are of the type Complex described in
the previous question,
6.2. INSTANCE METHODS 229
class Circle
{
double x; // x-coordinate of centre
double y; // y-coordinate of centre
double r; // radius
}
6. Write a main method that uses the Circle class developed in the
previous question. The main method should perform the following
actions.
(a) Create two Circle objects c1, representing the circle with centre
(4, −1) and radius 3, and c2, representing the circle with centre
(3, −2) and radius 5.
(b) Find and print the area of c1.
230 CHAPTER 6. CLASSES AND OBJECTS
(c) Determine the smaller of c1 and c2 and then print its centre
and radius.
(d) Determine whether or not c2 lies entirely within c1 and print
an appropriate statement.
6.3 Constructors
Example 1
Suppose, in the class Fraction, we created the following constructor:
public Fraction (int n, int d)
{
num = n;
den = d;
}
Then, in our main method, say, we could write
Fraction f = new Fraction(2,3);
This would declare f to be of type Fraction, create an object of type
Fraction, set f to refer to that object, and initialize the object to represent
the fraction 23 .
6.3. CONSTRUCTORS 231
2. Unlike every method that we have seen so far, the constructor method
does not specify a return type (not even void). It is implicit that the
method returns an object of type Fraction.
4. The call to the constructor method differs from other calls to in-
stance methods in that it does not use the dot notation. We wrote
f = new Fraction(2,3); rather than new f.Fraction(2,3);
Java does not restrict us to having only one constructor method for a
class. Like any other methods, constructors can be overloaded with each
version having its own signature and each doing some different kind of
initialization.
232 CHAPTER 6. CLASSES AND OBJECTS
Example 2
The following constructor for the Fraction class could be used to create a
Fraction object that has the same values as an existing Fraction object.
public Fraction (Fraction f)
{
num = f.num;
den = f.den;
}
We can even replace the default constructor with one that does the
kind of initialization that we want. For our Fraction class, the default
constructor will, given the call
Fraction f = new Fraction();
assign both the num and den fields the value zero, so that the resulting
object represents the indeterminate fraction 00 . If we wanted to, we could
replace this by a constructor that creates a fraction that represents 10 .
Example 3
0
A constructor that initializes Fraction objects to represent 1
would take
the following form:
public Fraction()
{
num = 0;
den = 1;
}
This constructor could be invoked by the call
Fraction f = new Fraction();
just as the default constructor is called.
To show how these methods could be used, consider the following fragment:
Fraction p = new Fraction(3,5);
Fraction q = new Fraction(p);
Fraction r = new Fraction();
6.3. CONSTRUCTORS 233
The first statement uses our first constructor to create an object repre-
senting the fraction 53 . The second statement creates another object that
also represents the fraction 35 . The third statement creates an object that
represents the fraction 10 . The diagram illustrates the results.
p q
Fraction Fraction
- num 3 - num 3
den 5 den 5
r
Fraction
- num 0
den 1
We said, at the start of this section, that Java supplies a default con-
structor if we do not write our own. However, if we do create our own
constructor(s), the default no longer operates. In such a situation, if we
want to have a constructor that has no parameters and does only basic
initialization, we must write it ourselves.
Example 4
The constructor
public Fraction ()
{
num = den = 0;
}
performs the actions of the default constructor for the Fraction class.
234 CHAPTER 6. CLASSES AND OBJECTS
Example 5
Suppose that a Student class has fields for a name, student number, and
number of credits. Normally, a new student will not have any credits. The
constructor for such a student might take the form
Sometimes a new student will already have some credits from another
school. To handle such cases, we could write another constructor.
Notice that the two constructors have two lines of code that are identical.
It is usually a good idea to avoid such repetition of multiple lines of code
because, if a mistake is found or if the program is being modified at a later
time, then the code must be changed in more than one place. We can avoid
such repetition here by rewriting the first constructor as follows.
Exercises 6.3
1. (a) Extend the definition of the Fraction class that we have been
developing to include the constructors discussed in this section.
(b) Write a main method that first constructs two Fraction objects
representing the fractions 75 and 83 and then creates two other
Fraction objects whose values are the sum and product of the
original fractions. The method should produce no output; we
will deal with printing of objects shortly. Use the methods plus
and times from Section 6.2 in your method.
class Circle
{
double x; // x-coordinate of centre
double y; // y-coordinate of centre
double r; // radius
}
that we decide to maintain the objects of our Fraction class in this way.
We could start by writing a constructor that creates fractions in the desired
form.
Example 1
The following constructor method for the Fraction class takes two int pa-
rameters, the numerator and denominator of a fraction. The constructor
ensures that the resulting fraction is represented with a positive denomi-
nator.
public Fraction (int n, int d)
{
if (d == 0)
throw new RuntimeException
("Attempt to construct Fraction n/0");
else if (d < 0)
{
num = -n;
den = -d;
}
6.4. HIDING INFORMATION 237
else
{
num = n;
den = d;
}
}
Notice that a denominator of zero is considered to be an error because
the resulting fraction is undefined. If this occurs, the constructor throws
an exception. For now, if this exception were to occur during execution
of a program, it would print the indicated message and execution of the
program would terminate. Exceptions and exception handling are discussed
in Appendix E.
Example 2
The following method allows a user outside the Fraction class to access
the value of the num field of a Fraction object.
public int getNumerator ()
{
return num;
}
238 CHAPTER 6. CLASSES AND OBJECTS
To use this method to obtain the value of the num field of a Fraction object
f, we could write
int n = f.getNumerator();
Methods like the one in Example 2 that allow us to gain access to the
values in private fields are, unsurprisingly, called accessor methods. The
identifier that we gave to the accessor method in Example 2 illustrates a
commonly used form; in Java, an accessor method for a field named stuff
is usually called getStuff.
Now, if we are to declare fields as private, what do we do if we want to
modify them from outside the class in which they are defined? The answer
is to write methods that can modify the fields for us, in carefully controlled
ways. Such methods are sometimes called mutator methods because they
alter or mutate the values of fields. Customarily, in Java, a method used
to set the value of a field named stuff would be called setStuff.
The process of creating programs so that information within a class is
made inaccessible from outside the class is called encapsulation. We will, in
the coming chapters, not only encapsulate data but also methods. In doing
so, we should be able to make our programs more reliable because the data
and methods in a class can be tested thoroughly with each other and then
only communicate with the outside world in very carefully controlled ways
that cannot (if we have done our job well) introduce incorrect results into
the class.
Example 3
The method invert replaces a Fraction object by its inverse while main-
taining the positive denominator property.
den = -den;
num = -num;
}
}
}
Exercises 6.4
1. Explain the difference between an accessor method and a mutator
method.
2. Study the following fragment and then answer the question that fol-
lows it. (The dots indicate code that we have omitted.)
class Sample
{
private int i;
that the circle’s radius is not negative by changing the sign of any
negative radius parameters.
Example 1
Suppose, once again, that we are using our Fraction class and that we
have created an object f by writing
Fraction f = new Fraction(2,5);
to create the object shown in the next diagram
f
Fraction
- num 2
den 5
If we now write
Fraction g = f;
this copies the value from the reference f to the reference g, producing the
result shown in the next diagram where f and g refer to the same object.
6.5. COMPARING AND DISPLAYING OBJECTS 241
f g
Fraction
- num 2
den 5
Example 2
If we first create f as we did in the previous example, and then wrote
Fraction g = new Fraction(f);
this would create a new object whose instance fields have the same values
as those of the original. (We are assuming here that we have written an
appropriate constructor, as shown in Example 2 of Section 6.3.) The result
of this statement is shown in the next diagram.
f g
Fraction Fraction
- num 2 - num 2
den 5 den 5
Example 3
If f and g are as shown in the diagram,
f g
Fraction Fraction
- num 3 - num 3
den 4 den 4
then the expression f == g would have the value false because f and g
refer to distinct objects stored in different locations in memory. The fact
that each of those objects represent the same fraction is irrelevant.
Example 4
The following method will return true if and only if two Fraction objects
have identical fields. The method first checks that the explicit parameter
object is not null.2 If it is not, the method then checks that each of the
fields of the two objects are equal.
2 There is no need to check that the implicit object is not null as Java requires that
If you look carefully at this method, you will notice that it returns true
when the boolean expression that controls the if statement is true and it
returns false when that expression is false. We can take advantage of
this observation to shorten the method definition by simply returning the
value of the expression.
An equals method need not require that all fields be equal. The
method can apply whatever criteria we choose to consider for equality. For
objects of the Fraction class, for example, we might consider that two
objects are equal if the ratios of the num and den fields of the objects are
equal.
If we do not write our own equals method, Java supplies a default
version for us, for any type of object that we define. Unfortunately, Java’s
default equals method is fairly useless as it only uses == as its criterion
for equality. To get something more useful, we must override the default
method by writing our own as we have in Example 4.
To display values, we have been using the methods print and println.
These methods automatically convert primitive values (like int or double)
to String values for printing. If we want to display objects, we can do so in
the same way. For any class, Java automatically calls an instance method
toString provided for this purpose.
244 CHAPTER 6. CLASSES AND OBJECTS
Example 5
Consider the following main method:
On the computer on which this book was written, on the day on which this
section was written, this method produced the output
Fraction@1cc7c5
Example 6
Suppose we add the following method to our Fraction class.
Now, if we were to run the program in the previous example, Java would
use our toString method rather than the default. The output from the
program would be
2/3
the numerator and denominator of the object in a form that looks like a
fraction.
6.5. COMPARING AND DISPLAYING OBJECTS 245
For each class that you create, you should write a toString method
that overrides the default method. Even if you don’t plan to use such
methods in your programs, they can be very useful for debugging while
you are developing a program involving objects. For each case, choose a
form for the string that makes the information clear.
Exercises 6.5
1. If p and q are both variables of type Fraction, under what circum-
stances will the expression p == q have the value true?
(a) Draw a diagram like those shown in the text to illustrate this
situation.
(b) If the statement p = q; is executed, draw a diagram to illustrate
the result.
3. The diagram shows a Circle object of the type that we have been
using in exercises throughout this chapter.
c1
Circle
x 2
- y 3
r 1
Example 1
The class Fraction can be extended to contain an addition method as
follows:
class Fraction
{
private int num;
private int den;
Suppose now that we want to call this method from within the main
method of a program. The way that we invoke the method depends on the
way in which we organize our program. If we were to put the main method
in the Fraction class, then we would call this method in the same way
that we did in Chapter 5 using expressions of the form
<method identifier>(<parameter list>)
For example, assuming that f, g, and h have all been declared to be of type
Fraction in the main method, then the statement
f = sum(g,h);
would assign to f the sum of the current values of g and h. Notice in the
call the lack of an object and a dot preceding the method name. When we
call a class method, there is no longer an implicit object parameter; only
explicit parameters are possible.
Usually we do not organize our programs so that all methods are in the
same class. If the main method were in a class separate from the Fraction
class, then a call to any class method in the Fraction class would require
a different form. Suppose that our main method, in a class other than the
Fraction class, contains three Fraction objects: f, g, and h. Now, to
invoke sum to add g and h storing the result in f, we could write
248 CHAPTER 6. CLASSES AND OBJECTS
f = Fraction.sum(g,h);
Here the expression that is used to invoke the sum method has the same
form as those used to invoke the class methods in both the Math class and
the In class:
<class identifier>.<method identifier>(<parameter list>)
The call first directs Java to the correct class and then to the appropriate
method within that class.
Exercises 6.6
1. Consider once again the class Circle that we have seen a number of
times in this chapter. Each of the following headers could be used in
methods that could be placed in the Circle class to determine and
return the area of a circle.
A: public static double area (Circle c)
B: public double area ()
(a) Identify the class method and the instance method giving rea-
sons for your choices.
(b) Assuming that a Circle object called disc has been created and
initialized in a class outside the Circle class, write a statement
that could be used to assign to the double variable discArea
the value of the area of disc using method A.
(c) Repeat the previous part using method B.
(d) Complete the definitions of each of the methods.
(e) Write a definition of a class method called areaRatio that has
two parameters, both of type Circle. The method should com-
pute and return, as a double value, the ratio of the area of the
first circle to the second.
2. Write definitions for the following class methods that could be used
in the Fraction class.
(a) The method product should have two Fraction parameters.
It should return a value of type Fraction, the product of the
parameters passed to it.
(b) The method abs should have a single Fraction parameter. It
should return a value of type Fraction in which any negative
fields in the parameter have been replaced by their absolute
values.
6.7. CLASS FIELDS 249
Example 1
If we are creating a class Car, we might want to have fields to keep track of
fuel consumption rate, distance travelled, and price of gasoline. The first
two fields will vary from one car to another and should therefore be instance
fields. If all cars in the class use the same grade of gasoline, the price will be
the same for all cars. Consequently, there is no point in keeping a separate
copy of the price of gas for every Car object. Instead, we keep it as a class
field so that there is only one copy for the entire class. The declarations of
these fields might take the following form.
class Car
{
private double consumptionRate;
private double distance;
private static double gasPrice;
}
250 CHAPTER 6. CLASSES AND OBJECTS
Exercises 6.7
1. What is the difference between the declaration of a class field and
that of an instance field?
4. Assume that fields in a class have been declared with the public
modifier. From outside that class, how would we refer to
(a) a class field? (b) an instance field?
Example 1
In the Fraction class that follows, we implement various constructors,
operations for performing arithmetic, a method, getFraction, for creating
new Fraction objects from input supplied by a user, a toString method
for printing fractions, and accessor methods that allow us to retrieve values
of private fields. The size method gives the magnitude of a fraction
while equals can be used to compare two fractions. The reduce method
is used to ensure that all fractions are maintained in reduced form with
non-negative denominators. It has been made private because it is not
needed outside the class; all Fraction objects are created and maintained
in reduced form within the class.
class Fraction
{
private int num;
private int den;
252 CHAPTER 6. CLASSES AND OBJECTS
public Fraction ()
{
// Create an object representing 0/1.
num = 0;
den = 1;
}
else if (num == 0)
return "NaN";
else if (num > 0)
return "Infinity";
else
return "-Infinity";
}
{
den *= -1;
num *= -1;
}
Once we have created the Fraction class, we can then use it in pro-
grams that manipulate fractions in an easy and natural way. The next two
examples show the use of some of the methods of the Fraction class.
Example 2
The following program prompts the user for two fractions and then finds
and prints their sum, difference, product, and quotient.
class FractionArithmetic
{
public static void main (String[] args)
256 CHAPTER 6. CLASSES AND OBJECTS
{
Fraction first = Fraction.getFraction
("Enter the first fraction");
Fraction second = Fraction.getFraction
("Enter the second fraction");
System.out.println("sum is "
+ first.plus(second));
System.out.println("difference is "
+ first.minus(second));
System.out.println("product is "
+ first.times(second));
System.out.println("quotient is "
+ first.dividedBy(second));
}
}
If a user responds to the prompts by supplying fractions whose values are
3 5
4 and 8 , the program will respond by printing
sum is 11/8
difference is 1/8
product is 15/32
quotient is 6/5
Example 3
The following program prompts the user for a value of n and then finds
and prints the value of the expression
n
X 1 1
×
i i+1
i=1
class SeriesSum
{
public static void main (String[] args)
{
System.out.println("Enter a value of n");
int n = In.getInt();
6.8. PUTTING THE PIECES TOGETHER 257
Exercises 6.8
1. The equals method of the Fraction class is used to test for equality
of Fraction objects. Why do we not simply use the == operator to
make this test?
2. In the reduce method of the Fraction class, the index of the for
statement gets smaller with each iteration until it gets to two. Would
the method work correctly if the index started at two and became
larger on each iteration? Explain.
3. Write a program that has a main method that uses the Fraction class
to solve the following problem: prompt the user for three fractions, a,
b, and c, compute the value of ab − c2 , print the result, and print the
sum of the squares of the numerator and denominator of the result.
4. Write a program that uses the Fraction class to find and print, as
fractions in lowest terms, the values of the expression
n
X 1 1
−
2i − 1 2i + 1
i=1
for n = 1, 2, . . ., 10.
5. Use your results from the program of the previous question to con-
jecture the value of
1000
X 1
1
−
i=1
2i − 1 2i + 1
Avoiding Errors
1. With the introduction of instance and class fields in this chapter, we
have now seen four varieties of “variable” in Java. It is important that
you be able to distinguish them and to be aware of the differences in
the ways that they are used and behave.
make your intentions clearer to your readers to write such a call using
an expression of the form
this.<method identifier>(<parameter list>)
Debugging
Exercises 6.9
1. Under what circumstances does Java supply a default constructor for
a class?
3. The following method for the Fraction class that we have been study-
ing throughout this chapter uses the reduce method introduced in
Section 6.8. Study the method and then answer the questions that
follow it.
2. What is the difference between the declaration of a class field and the
declaration of an instance field?
Complex
- re a
im b
(a) private double fields re and im for the real and imaginary
parts of a number,
(b) a constructor method that could be called by the statement
Complex z = new Complex(2.7,-1.1);
to create an object that represents the complex number 2.7 −
1.1i,
(c) a constructor method with no parameters that creates a Complex
object representing the number 0,
(d) an instance method that finds the modulus of a complex number,
(e) an accessor method that returns the value of the real part of a
complex number.
7. Write a class Lock that could be used to create electronic lock objects.
Each lock may be in either an open (unlocked) or a closed (locked)
state and each one is protected by its own integer key which must be
used to unlock it. The class should contain the following methods.
The following main method illustrates how the Lock class should
work.
lock1.close();
lock2.close();
Projects
class Rectangle
{
private int left; // x-coordinate of left edge
private int bottom; // y-coordinate of bottom edge
private int width; // width of rectangle
private int height // height of rectangle
i2 = j 2 = k 2 = ijk = −1
ij = −ji = k
jk = −kj = i
ki = −ik = j
q = w + xi + yj + zk
q = [w, x, y, z]
q = (s, ~v ), where s = w, ~v = [x, y, z]
3 Up to now, all classes that we have been dealing with in Java’s API have been in
the package java.lang but the BigInteger class is in the package java.math. Because
of this, we have to take special action in order to be able to use its fields and methods.
For instructions on how to use classes in packages outside java.lang, see page 368.
268 CHAPTER 6. CLASSES AND OBJECTS
q1 + q2 = [w1 + w2 , x1 + x2 , y1 + y2 , z1 + z2 ]
• Multiplication
The product of quaternions q1 and q2 is:
q1 q2 = (s1 s2 − ~v1 · ~v2 , s1~v2 + s2 ~v1 + ~v1 × ~v2 )
where ~v1 · ~v2 (the dot product) and ~v1 × ~v2 (the vector product)
are defined as follows:
~v1 · ~v2 = x1 x2 + y1 y2 + z1 z2
~v1 × ~v2 = [y1 z2 − z1 y2 , z1 x2 − x1 z2 , x1 y2 − y1 x2 ]
• Scalar Multiplication
A quaternion q can be multiplied by a real number to give a
quaternion result. If c ∈ R and q = w + xi + yj + zk, then
• Conjugation
If q = w + xi + yj + zk, then the conjugate of q is
q∗ = w − xi − yj − zk
• Finding Magnitude
The size or magnitude of a quaternion q is
p
||q|| = w 2 + x2 + y2 + z 2
• Inversion
The inverse of a quaternion q is
q∗ 1 ∗
q−1 = = q
||q|| ||q||
6.10. REVIEW EXERCISES 6 269
p
~ = [x, y, z]
and p = (0, p
~)
θ
Then let s = cos
2
θ
and ~v = û sin
2
and q = (s, ~v )
p0 = qpq−1
p0 = (0, ~p 0 )
You are to create two classes in two different files. In a file called
Vector3.java, you should create fields and methods to implement
three-dimensional vectors. This class should begin with the following
field definitions:
class Vector3
{
private double x;
private double y;
private double z;
..
}
270 CHAPTER 6. CLASSES AND OBJECTS
You must also write a number of methods for the class. These should
include (but are not limited to) the following:
• A constructor with three double parameters representing the
fields x, y, and z.
• A toString method that, for a vector with components x, y,
and z, returns a string of the form:
"[x,y,z]"
• Accessor methods getX, getY, and getZ that return the values
of x, y, and z respectively.
• An instance method dotTimes with one explicit parameter of
type Vector3. The method should return the value of the dot
product of its implicit and explicit parameters.
• An instance method vecTimes with one explicit parameter of
type Vector3. The method should return the value of the vector
product of its implicit and explicit parameters (in that order).
• An instance method scaleTimes that has one double param-
eter. The method should return a vector that is the result of
multiplying the double parameter by the implicit vector object.
• An instance method unit with no explicit parameters. The
method should return a normalized (unit) vector that has the
same direction as the implicit parameter. If the parameter repre-
sents the zero vector, the method should return the zero vector.
In a separate file, called Quaternion.java, you are to create a class
to implement quaternions. This class should begin with the following
field definitions:
class Quaternion
{
private double w;
private double x;
private double y;
private double z;
..
}
Q: Your Master’s degree was in the area of operations research (OR). Could
you explain that term?
A: In business, it is the use of mathematics to help solve decision problems
and optimize solutions to problems.
Q: What was your first job after you finished school?
A: I went to work for a company called Numetrix that was using OR tech-
niques to create software for scheduling and planning. They were selling
the software to a variety of large companies where it was used to optimize
the scheduling of production in plants as well as the shipment and storage
of products in locations around the country.
Q: How long did you stay with them?
A: I was there for seven years, doing a wide variety of jobs at different times.
I started in consulting but later I was involved in implementing a large
piece of software, customer support, technical writing, staff training, and
managing software development. Having such a variety of jobs is probably
a fairly common thing in the software world.
Q: And then?
A: Then I changed industries completely to a job in the telecommunications
field with a company called Solect that built customer care and billing
systems for internet service providers. Despite the fact that the setting
was very different, I could still use many of the skills that I had developed
previously. I did a lot of work for them with British Telecom — involving
a lot of travel back and forth to Britain.
Q: And after that?
A: Then I moved to a small company called Drums that was later acquired
by a larger company based in Boston called ChannelWave. While I was
with ChannelWave, I was director of product management. They produce
272
Customer Relation Mangement (CRM) systems that help companies in
tracking and optimizing their sales and distribution channels. This can
be a very complex problem, particularly if a company sells to resellers or
interacts with its customers in a variety of ways — call centres, web sites,
field representatives, and so on.
Q: What is your current job?
A: I am an independent consultant, currently on a one-year contract with
Kraft Foods. To help promote their products, they publish recipes and
a magazine for their customers. Currently they are involved in an inter-
esting project in which they are attempting to react individually to their
customers’ needs and desires, giving different recipes and different versions
of the magazine to different customers based on customer profiles. I am
working on the day-to-day operation of Kraft’s CRM system and creating
enhancements to that system.
Q: What are the best features of being an independent contractor?
A: Normally, to get to a senior position with a senior salary, one has to get
into mangement but I really enjoy working directly with the data rather
than managing. As a contractor, I get to do absolutely fascinating work
(at a very good rate of pay) without the politics involved in being part
of management. I also like the freedom to be able to pick and choose the
things that I want to do.
Q: Is there a downside?
A: The primary one is the uncertainty. After the current contract expires, I
have to find another one. Also, as a consultant I have no benefits and I
have less security than a regular employee.
Q: Do you have any advice for those starting out in computer studies?
A: Don’t try to plan too much because, with the rate at which things change,
you don’t really have a clue about what you are going to be doing in five
or ten years. Choose something that you think you will like and look for
opprtunities while you are there.
273
Chapter 7
Object Interaction
Example 1
The diagram illustrates the class-hierarchy relationships of the classes that
we have been discussing.
1 Some languages do permit classes to extend more than one class. This feature is
known as multiple inheritance.
7.1. CLASS HIERARCHIES 277
Animal
6
Vertebrate
6
Mammal
6
Dog
6
Each class in the diagram inherits from all the classes in the chain
above it so that, for example, Dog inherits from Animal, Vertebrate,
and Mammal. The top class, Animal, inherits from no other class. In
Java, the class that has this property is the class Object in the pack-
age java.lang. All other classes inherit from Object — either directly, by
extending Object, or indirectly, by extending some other class that inherits
from Object. To indicate in Java that a class extends another class, we
use the word extends in the class header. If Samoyed and Dog were Java
classes, the definition of Samoyed would begin
class Samoyed extends Dog
If no superclass is specified by an extends clause, Object is the default
superclass.
The classes from which a class inherits are sometimes referred to as its
ancestors. The classes that inherit from a class are sometimes referred to
as its descendants. Thus, in Example 1, the descendants of Mammal are Dog,
Samoyed, Husky, and Beagle while the ancestors of Mammal are Vertebrate
and Animal.
What does it mean to say that a class “inherits” from another class?
Essentially, it means that the fields and methods of the superclass are, in
a sense, part of the subclass. To illustrate the relationships of objects in
a hierarchy, in this chapter we will be drawing objects in a style that is
somewhat different from that used previously. To illustrate this new style,
278 CHAPTER 7. OBJECT INTERACTION
Example 2
Suppose that we begin to define a class Person by writing
class Person
and that we begin to define a class Student by writing
class Student extends Person
Since we did not state that Person extends any class, then by default it
extends the root class Object. Diagrams to illustrate objects of the classes
Object, Person, and Student could take the following form.
An Object A Person A Student
Person Person
Student
Notice how the diagrams reflect the inheritance relations. Any Person
object has both an Object part and a Person part. Any Student object
has an Object part, a Person part, and a Student part.
Exercises 7.1
1. (a) In the class-hierarchy diagram shown in Example 1, what is the
superclass of Mammal?
(b) What class or classes does Dog extend?
7.2. INHERITANCE AND VARIABLES 279
5. (a) Draw a diagram, like the one in Example 1, showing the hi-
erarchical relationship among the following classes: Rectangle,
Polygon, Quadrilateral, Square, Object, Triangle, and Shape.
(b) Draw a diagram, like one of the diagrams in Example 2, to show
the structure of a Quadrilateral object.
Example 1
If we have a Student object, as discussed in the previous section, it contains
parts that are of three different types (Object, Person, and Student).
Object
Person
Student
Here, all of the following statements are valid (assuming that the appro-
priate constructors exist).
Since the type of a reference variable and the type of the object to
which it refers need not be the same, we have to be careful with assignments.
We can make assignments within the same hierarchy but only if they are
appropriate. Using our Student class again, the following fragment is valid.
The cast in the second statement will make the compiler happy. However,
since o is of type Object, it does not have a Student part and so it should
not be assigned to s, a variable of type Student. Although the fragment
will compile, when we attempt to execute the compiled code, it will throw
a ClassCastException error.
As we saw in Chapter 6, fields that are declared with the private
attribute can only be seen from within their own class. This still holds
even for classes in the same hierarchy. If you want to work with a private
field in another class in the same hierarchy, you must still use accessor and
mutator methods. For situations in which you want subclasses to have
direct access to fields of a class, Java offers another visibility modifier:
protected. Any field declared with the protected attribute can be seen
either in that class or any of its subclasses but not elsewhere.
Example 2
Suppose that a class definition starts as follows. (The dots indicate other
features of the class that we have not shown here.)
class Sample
{
public int a;
float b;
protected char c;
private boolean d;
...
}
Here
• a is visible everywhere
Exercises 7.2
1. Consider the class hierarchy shown here and the statements that fol-
low it. State, with reasons, which of the statements are valid and
which are not. (Assume that appropriate constructors exist.)
Object
6
Vehicle
6
MotorVehicle
6
LightTruck HeavyTruck
A Fraction Object
Object
toString()
equals(Object o)
...
Fraction
toString()
equals(Fraction f)
...
Example 1
If we were to write
Fraction f = new Fraction(2,3);
then the call f.toString() would invoke the toString method of the
Fraction class, not the version in the Object class. If we had not written
a toString method for the Fraction class, then Java would have started
working its way up the class hierarchy looking for a method with the ap-
propriate signature. In this case, the next class up the hierarchy is Object
where the default version of toString resides.
Exercises 7.3
1. Referring again to the Object – Person – Student hierarchy, suppose
that we have a method defined in the Person class with the header
public String getName ()
and a method defined in the Student class with the header
public String getNumber ()
Now consider the following fragments from a main method. State
which ones are valid, which ones are not valid but can be fixed by
a cast, and which ones are not valid and cannot be fixed by a cast.
Assume that all constructors are valid.
class A
{
public A ()
{System.out.println("A part");}
public void m ()
{System.out.println("A’s m");}
}
2 To save space, we have partially abandoned our usual indentation scheme.
288 CHAPTER 7. OBJECT INTERACTION
class B extends A
{
public B ()
{System.out.println("B part");}
public void m ()
{System.out.println("B’s m");}
}
class Sample
{
public static void main (String[] args)
{
A a1 = new A();
B b1 = new B();
A a2 = (A)b1;
a1.m();
a2.m();
b1.m();
}
}
Recall that, to refer to the current implicit object of a class, we used the
reserved word this. In a similar way, we can refer to the superclass of
a class by using the reserved word super. This is often used to invoke a
constructor of the superclass. If, in a constructor of a class, we want to
call the constructor of its superclass with some argument, we can do so. If
such a call is present, it must be the very first statement in a constructor.
Example 1
Looking again at our Person and Student classes, suppose that we add
the following field and constructor to the Person class. The dots indicate
other features of the class that we have not shown here.
class Person
7.4. USING SUPER AND INSTANCEOF 289
{
private String name;
public Person (String name)
{
this.name = name;
}
...
}
Now, suppose that an object of the Student class has a student number
as well as a name. We could modify our Student class to provide an
appropriate constructor as follows.
Example 2
Suppose that we want to write toString methods for our Person and
Student classes that we have been developing. The toString method of
the Person class might return the value of the name field defined in that
class. Thus, in Person, we might have
Example 3
Suppose that the Dog class contained
class Dog
{
protected String name = "Dog";
public void feed ()
{
...
If we were writing code within the Samoyed class, and we wanted to use the
name field of the Dog class, we could write super.name to do so. Similarly,
the expression super.feed(), used in an instance method in the Samoyed
class, would invoke the feed method of the Dog class for the current implicit
Samoyed object.
Example 4
Suppose once again that the class Student is a subclass of the class Person.
Suppose also that all Person objects have names (that are returned by the
method getName in the Person class) but only Student objects have stu-
dent numbers (that are returned by the method getNumber in the Student
class). We can then write
Person p;
292 CHAPTER 7. OBJECT INTERACTION
to create a variable of type Person that could refer to either a Person ob-
ject or a Student object. After p has been assigned some value, we might
use the following fragment to print information about the object referred
to by p.
System.out.println(p.getName());
if (p instanceof Student)
System.out.println(((Student)p).getNumber());
The last line illustrates a situation similar to one that we saw previously, in
Section 7.3. Knowing from the second last line that p is currently referring
to a Student object, we can safely print its student number. However, the
compiler does not know this (since it does not look at previous code) so we
must reassure it by casting p to a Student. The extra parentheses around
(Student)p are required because a method call has a higher precedence
than a cast.
Exercises 7.4
1. Explain the effect of placing the statement
super("man");
as the first statement of a constructor.
class A
{
public A () {System.out.print("A");}
}
class B extends A
{
public B () {System.out.print("B");}
}
If we then write
System.out.println(p1.toString());
System.out.println(p2.toString());
modifier abstract and we provide no body for the method in that class.
Instead, the various definitions are provided in the subclasses. If a class
contains any methods that are declared to be abstract, then the class must
also be declared to be abstract with the abstract modifier.
Example 1
To implement the ideas on interest computation outlined here, we could
create the following structures.
method in the abstract superclass. If it does not do so, then it must also
be declared to be abstract (and it cannot be instantiated).
Abstract methods can help to simplify code by eliminating the need
for casting. For the banking example, suppose that in a main method, we
write the following fragment.
Account a = new Savings();
double interest = a.computeInterest();
Previously we saw fragments like this in which the compiler required a
cast to assure it that the object is not of type Account (where there is
no definition of the method). Here, however, the compiler will not object
because there is no possibility that the object to which a refers could be of
type Account. This is because the Account class is abstract and, therefore,
it cannot be instantiated.
Exercises 7.5
1. If a method in a class is declared to be abstract, what else must be
done?
2. What would be printed by the following program?
class A
{
public void m1 ()
{System.out.println("A1");}
}
class B extends A
{
public void m1 ()
{System.out.println("B1");}
public void m2 ()
{System.out.println("B2");}
}
class C extends B
{
public void m2 ()
{System.out.println("C2");}
}
296 CHAPTER 7. OBJECT INTERACTION
class Driver
{
public static void main (String[] args)
{
A a1 = new A();
A a2 = new C();
B b1 = new B();
B b2 = new C();
a1.m1();
a2.m1();
b1.m2();
b2.m2();
}
}
Avoiding Errors
1. Try to avoid using the same code in different classes. Otherwise, if
you want to alter the code, you will have to do so in each of the
7.6. AVOIDING ERRORS AND DEBUGGING 297
class Sample
{
public double foo (int n)
{
...
}
...
}
...
}
...
}
The method in the subclass does not have the same signature as the
one in the superclass so it does not override it; it overloads it.
3. Many people argue that using protected visibility is an invitation to
trouble. If a field is specified as being private, then it cannot be seen
from outside its own class. If we have thoroughly tested the class, we
can be reasonably sure that it will behave as we expect it to do. If,
on the other hand, it is specified as being protected, then it can be
altered from any subclass of the class in which it is declared. Such
alteration might be done by someone other than the original writer
and/or at a much later time than the original code was written. In
either case, the value of the field could be easily corrupted. This can
be avoided by always declaring fields as private and only permitting
access to them by carefully controlled accessor and mutator methods.
Debugging
1. If a field does not have the correct value, look for inadvertent shad-
owing of that field. When a class inherits from another class, it may
have additional fields but, normally, it will not have fields whose iden-
tifiers are the same as those in an ancestor class. For example, if we
have
class Person
{
protected String name;
...
}
and
Exercises 7.6
1. Study the following program and then answer the questions that fol-
low it.
class A
{
public void m (double n)
{System.out.println(2*n);}
}
class B extends A
{
public void m (int n)
{System.out.println(3*n);}
}
class Driver
{
public static void main (String[] args)
{
B b = new B();
b.m(2);
b.m(2.0);
}
}
• author
(b) Write a constructor for the class Book with header
public Book (String title, String author)
(c) Write a constructor for the class History with header
public History (String title, String author,
String timePeriod)
(d) Write a toString method for the Book class.
(e) Write a toString method for the History class.
8. What would be printed by the following program?
class A
{
public void m ()
{
System.out.println("A");
}
}
class B extends A
{
public void m ()
{
super.m();
System.out.println("B");
}
}
class Driver
{
public static void main (String[] args)
{
A a1 = new A();
A a2 = new B();
B b1 = new B();
a1.m();
a2.m();
b1.m();
}
}
302 CHAPTER 7. OBJECT INTERACTION
{
return name;
}
}
alive = false;
}
public String toString ()
{
return super.toString() + " is " + getStatus();
}
}
10. Study the following classes (all of which compile correctly) and then
answer the questions that follow them.
class Vehicle
{
private int numWheels;
public Vehicle (int nw)
{
numWheels = nw;
}
public int getNumWheels ()
{
return numWheels;
}
}
Projects
Arrays
The table shown below gives the average Canadian retail price for one litre
of regular gasoline for the period from 1980 to 2000.
Year 1980 1985 1990 1995 2000
Price($) 0.27 0.51 0.59 0.56 0.72
0 1 2 3 4
price 0.27 0.51 0.59 0.56 0.72
Example 1
The temperatures of a solution over a six-minute interval are recorded in
the following table:
Time (min) 0 1 2 3 4 5 6
Temperature (◦ C) 32.0 27.2 24.4 22.7 21.6 21.0 20.6
An array temp could be used to record the temperatures of a solution each
minute as follows:
0 1 2 3 4 5 6
temp 32.0 27.4 24.4 22.7 21.6 21.0 20.6
The identifier of the array is temp.
The identifiers of the elements are temp[0], temp[1], . . . , temp[6].
The values of the elements are 32.0, 27.4, . . . , 20.6
The indices are 0, 1, . . . , 6.
price
0 1 2 3 4
- 0.0 0.0 0.0 0.0 0.0
Notice the value 0.0 shown in each element of the array. Arrays are
automatically initialized when we allocate space to them. If the elements
310 CHAPTER 8. ARRAYS
are numeric, they are initialized to zero (of the appropriate type); if they are
char values, they are initialized to the null character with representation
consisting of 16 zero bits; if they are boolean they are initialized to false;
if they are references, they are initialized to null.
Again, as with any object, we can combine the declaration of the array
reference variable and the allocation of memory into one statement. For
the price array, we could have written
double[] price = new double[5];
to declare, allocate memory for, and initialize the price array in one state-
ment.
This can be done in general. A statement of the form
<type>[] <identifier> = new <type>[<expression>];
declares that <identifier> is an array whose components are of type <type>
and also allocates space in memory for such an array.1 The size of the array
is determined by the value of <expression>. This is often simply a positive
integer constant but it can be any expression that has a positive value and
is of any integer type other than long.
Example 2
The following fragment declares an int array list of size 20.
byte b = 10;
int[] list = new int[2*b];
Once an array has been declared, its size is fixed for the duration of
its existence. Every array object has a length field whose value can be
obtained by appending .length to the identifier of the array. The value of
an expression of the form <identifier>.length is the number of elements
in the array. Since arrays are always indexed from zero, the length of an
array is one greater than the highest index in the array.
Java has an alternative form of array declaration that allows us to
initialize an array with any values that we like. The next example illustrates
this feature.
1 Java also allows us to write array declarations in the form <type> <identifier> []
with the square brackets following the identifier but we will not be using that form.
8.1. TABLES AND ARRAYS 311
Example 3
The following statement declares an int array called primes and initializes
it with the values of the first ten prime numbers.
int[] primes = {2,3,5,7,11,13,17,19,23,29};
Notice in the example the absence of the keyword new and the use of
brace brackets rather than square brackets on the right side of the assign-
ment operator. In addition, the size of the array is not stated explicitly;
Java knows that the array is of length ten because there are ten values
inside the brace brackets. The values inside the brace brackets need not
be constants; they can be expressions which will be evaluated at the time
that the program is executed. The only restriction on these expressions is
that they all be of the same type since (as we said at the beginning of this
section) arrays are collections of values of the same type.
Although Java’s arrays are always indexed from zero, sometimes the
values that we are dealing with are more naturally indexed from some
other value. Suppose, for example, that we wanted to store the values of
a sequence that has terms t1 , t2 , . . . , tn . We could use an array of length n
but then ti would be stored in the location with index i-1. An alternative
(that we favour) is to declare the array to be of size n + 1 and then store
ti at location i with the location having index zero being unused.
Example 4
Suppose that we wanted to store the sequence with terms
1 1 1
t1 = , t2 = , . . . , t5 =
2 4 32
in an array of double values. We could create and initialize such an array
by writing
double[] terms = {0.0,0.5,0.25,0.125,0.0625,0.03125};
This would store ti at the location with index i. The location with index
zero would be unused.
312 CHAPTER 8. ARRAYS
Exercises 8.1
1. An array to store marks for twenty students has been declared as
follows:
int[] marks = new int[20];
(a) What is the array identifier?
(b) What is the identifier of the first element in the array?
(c) What value is stored in each element by the declaration?
(d) What is the value of marks.length?
(e) What are the indices of the array?
2. How much space (in bits) would be required to store the elements of
each array?
(a) int[] a = new int[20];
(b) double[] b = new double[100];
(c) float[] c = new float[50];
(d) boolean[] d = new boolean[1000];
3. Write declarations to create arrays that would be appropriate for
storing the indicated data.
(a) the numbers of votes cast for five candidates in an election
(b) the answers to a twenty-question true/false quiz
(c) average family size in the years 1900, 1910, . . . , 2000
4. (a) Write a statement that creates and initializes an array terms of
double values to store the terms of the sequence
1 2 6
t1 =
, t2 = , . . . , t6 =
2 3 7
(b) What is the value of terms.length?
5. The table gives atomic masses of the eight lightest elements listed
according to atomic number.
Atomic Number 1 2 3 4 5 6 7 8
Atomic Mass 1.0 4.0 6.9 9.0 10.8 12.0 14.0 16.0
8.2. USING ARRAYS 313
Having seen how to create arrays, let us now examine some of the ways
that we can operate on them and their elements.
The elements of an array can be treated like any other variable of that
type. We can assign values to them, print them, use them in expressions,
and so on.
Often we want to perform some operation on every element of an array.
We sometimes refer to this as indexing through the array.
Example 1
If we wanted to create an array called flags containing 100 boolean values
all set to true (rather than the standard initialization to false), we could
write
boolean[] flags = new boolean[100];
for (int i = 0; i < flags.length; i++)
flags[i] = true;
also the use of flags.length to determine the upper bound. This is more
reliable and probably clearer to a reader than using the value 100.
Finally, note the way that we have written the condition that con-
trols the loop — as i < flags.length. The value of flags.length is
the size of the array — the number of elements in it. As we have al-
ready noted, since an array is indexed from zero, the index of the last
element is one less than the value of its length. If we were to write the
condition as i <= flags.length, then we would be trying to access an
element off the end of the array. If we attempt to use an index value
that is out of the range of an array, Java rewards us by throwing an
ArrayIndexOutOfBoundsException.2
Because arrays, like other objects, are reference types, they behave in
similar ways when we try to manipulate them. As an example, suppose
that we make the following declarations:
int[] p = {1,3,5,7,9};
int[] q = {0,2,4,6,8};
to give us the situation pictured in the next diagram.
p q
- 1 3 5 7 9 - 0 2 4 6 8
p q
-
- 0
1 3 5 7 9 2 4 6 8
both p and q are referring to the same array cells. It is not a test of the
equality of the elements of two distinct arrays. If we wanted to test for
equality of the contents of two arrays, we could use a loop that indexed
through the arrays comparing corresponding pairs.3
Arrays, like all objects, can be parameters of methods and can be
returned by methods.
Example 2
The method join creates a new array that contains the elements of each
of the method’s parameters and returns a reference to that array.
public static double[] join (double[] a, double[] b)
{
double[] result = new double [a.length + b.length];
int i, j;
for (i = 0; i < a.length; i++)
result[i] = a[i];
for (j = 0; j < b.length; i++, j++)
result[i] = b[j];
return result;
}
As usual, Java passes parameters by value but, with arrays, the values
that are passed are references to the arrays. This means that methods have
the ability to alter the contents of arrays that are passed as parameters.
Example 3
The method swap shown here will switch the values of two elements in an
array of double values.
public static void swap (double[] list, int i, int j)
{
double temp = list[i];
list[i] = list[j];
3 Java contains a method Arrays.equals in the java.util package that compares
equality of content of arrays.
316 CHAPTER 8. ARRAYS
list[j] = temp;
}
To show the effect of swap, suppose we have an array masses shown below.
masses
Exercises 8.2
1. What would be printed by the following program fragment?
3. Write a method max that has one double array parameter. The
method should return the value of the largest element in the array.
row
0 1 2 29
- f f f f
To monitor sales for the entire auditorium, we want one of these arrays for
each row. We can do this easily by writing
boolean[][] sold = new boolean[25][30];
The results of this declaration are shown in the next diagram.
sold
? 0 1 2 29
sold[0] - f f f f
sold[1] - f f f f
sold[24] - f f f f
Although this may be surprising at first, analysis may make it seem per-
fectly reasonable. Recall that the general form of a declaration of an array
was
<type>[] <identifier> = new <type>[<expression>];
Comparing this form with our declaration of the sold array, the <type>
of the components of sold is boolean[]; each element of the sold array
is of type boolean[], a reference to an array of boolean values. As the
diagram indicates, the identifiers of these references are sold[0], sold[1],
. . . , sold[24].
Each sold[i] value refers to a boolean array, each of whose elements
are initialized to false (indicated by the letter f in the diagram). The
elements of the boolean arrays are identified by two indices — the first for
the index of the array reference and the second for the index of the element
8.3. MULTI-DIMENSIONAL ARRAYS 319
within its array. For example, the element in the upper left hand corner of
the diagram has identifier sold[0][0] while the element in the lower right
hand corner has identifier sold[24][29].
We could have created this structure in stages. As a first step, we
could have written
boolean[][] sold = new boolean[25][];
to create sold, a reference to an array of 25 variables of type boolean[].
As usual, when we create a new array, the values are initialized. Since the
type of the values is a reference type, each one is initialized to null.
sold
Now, to create the arrays to represent each row, we can use a loop.
for (int i = 0; i < sold.length; i++)
sold[i] = new boolean[30];
The loop would be executed 25 times (the value of sold.length). Each
time around, one more array of 30 elements would be generated and ini-
tialized. The final result would be what we want: 25 arrays of 30 boolean
elements representing the seats sold in the auditorium. An array of arrays
like this one is known as a two-dimensional array.
Often, we want to use two-dimensional arrays to represent values in a
simple rectangular table. In such cases, we can think of a two-dimensional
array as consisting of rows and columns. If we do this, it is customary to
think of the index representing a row preceding that representing a column.
Example 1
A table of int values with 3 rows and 6 columns can be created using the
declaration
int[][] table = new int[3][6];
As we have seen, this really creates three arrays each containing six ele-
ments but we can think of it as shown in the following diagram.
320 CHAPTER 8. ARRAYS
columns
0 1 2 3 4 5
0 0 0 0 0 0 0
rows 1 0 0 0 0 0 0
2 0 0 0 0 0 0
Example 2
Suppose we want to create a table with three rows and four columns, ini-
tialized as shown in the diagram.
5 2 7 3
6 8 7 9
4 5 2 3
on one line but we wrote it the way that we did to make the structure of
the array clearer to a human reader.
Example 3
The following fragment creates a triangular array of double values with
one element in the first row, two in the second, and so on for a total of five
rows.
Example 4
The statement
int[][] ragged = {{4,3,7},
{5,2},
{7,8,1,4}};
creates the array whose elements are illustrated in the following diagram.
4 3 7
5 2
7 8 1 4
Example 5
The following fragment could be used to find the sum of the elements of a
ragged two-dimensional array like the one shown in the previous example.
Notice that the upper bound of the inner loop now depends on the length
of each row of the array.
int sum = 0;
for (int row = 0; row < ragged.length; row++)
for (int col = 0; col < ragged[row].length; col++)
sum += ragged[row][col];
perhaps a book containing a number of tables, one on each page. The in-
dices could then be thought of as representing the pages, rows, and columns
(in that order). A four-dimensional array could then be thought of as a
number of volumes of books, all of the same size, with an index specify-
ing a volume, page, row, and column. As with two-dimensional arrays, we
need not specify all the dimensions in a declaration of a higher-dimensional
array.
Example 6
The following declarations are valid.
(a) char[][][] goodOne = new char[5][6][3];
This creates an array of 5 × 6 × 3 = 90 char elements or, more
accurately, five sets of six sets of three-element char arrays.
(b) byte[][][] goodTwo = new byte[20][][];
This creates 20 arrays of type byte[][]. Each of these 20 arrays
(called goodTwo[0], goodTwo[1] and so on) will be initialized to
null.
(c) double[][][] goodThree = new double[50][100][];
This creates an array of 50 references to arrays of 100 references to
arrays with elements of type double. These 5000 array references
will be set to null.
Example 7
These declarations are not valid.
(a) float[][] badOne = new float[][50];
This attempt to create an unspecified number of arrays that are each
of type float[50] will fail. The first dimension must always be
specified.
(b) boolean[][][] badTwo = new boolean[10][][25];
Although the first dimension has been specified here, the second and
third dimension show the same pattern that was exhibited in badOne.
324 CHAPTER 8. ARRAYS
Example 8
Suppose that a program had a method with the following header:
public static void process (double[] row)
Suppose also that in our main method, say, we had made the following
declaration:
double[][] table = new double[40][60];
We could then call process from main by writing
process(table[20]);
This works because table[20] is a reference to a one-dimensional array of
double values and that exactly matches the type of the parameter row.
Exercises 8.3
1. The diagram shows an array declared by the statement
int[][] a = new int[3][4];
State the identifier of each cell marked by a letter.
A
-B
-D
C
- E
- F
17
92
34
6. Write a method max that will return the maximum value of the ele-
ments in a two-dimensional array of int values. Do not assume that
the array is rectangular.
8. Write a method size that has one int[][][] parameter. The method
should return the number of elements in the array. Do not make any
assumptions about regularity of the array.
We said earlier that the elements of an array could be of any type. This
includes the possibility that elements can be objects. To illustrate this,
consider a class that could be used to represent complex numbers. We
could begin to define such a class by writing
class Complex
{
private double re;
private double im;
}
Assuming that the Complex class has a toString method, we could print
the value of the fifth object in the array by writing
8.4. ARRAYS OF OBJECTS 327
System.out.println(points[4]);
Objects can be very useful in situations that call for a number of arrays
of the same size. As an example, suppose that we are collecting data on
a group of children. We might have an array name to store their names,
another array age to store their ages in years, a third array height to
store their heights in metres, and a fourth array sex to store their sex as a
character (M or F). These could be declared as follows:
Arrays of the same length, carrying data on different aspects of the same
problem, are sometimes called parallel arrays. Rather than using four
parallel arrays for our study of child development, we could use a single
array of objects.
Example 1
We could begin to define a class that could be used for a study of children’s
development as follows:
class Child
{
private String name; // family, given
private int age; // in years
private double height; // in metres
private char sex; // M or F
}
• A method that processes data about one object can be given a single
object parameter rather than many item parameters.
• If we modify the program later to include different kinds of data, we
need only change the fields of the object class; no parameters need
to be added to our methods.
• By making the fields private, we can ensure (by using well-tested
accessor and mutator methods) that the data will be handled appro-
priately.
Although the elements of an array must all be of the same type, if we
are dealing with objects, we can use some of the ideas developed in Chapter
7 to get around this restriction and store objects of various types in the
same array.
One way to do this is to declare an array to be of type Object. Since
Object is the superclass of all object classes, an object of any type must
have an Object part and can, therefore, be stored in an array of type
Object. The actual type of an element at execution time can be determined
by using the instanceof operator.
Another approach is to create an abstract superclass of all the classes
whose objects we want to have in an array. This is done in the next example
where objects representing various shapes are stored in the same array.
Example 2
Suppose we have objects of type Circle and type Square that we want
to store in the same array. We could do so if we create an abstract class
Shape whose partial definition might be
abstract class Shape
{
public abstract double area ();
...
}
We could then begin to define the Circle and Square classes as follows.
class Circle extends Shape
{
private double radius;
public double area ()
8.4. ARRAYS OF OBJECTS 329
{
return Math.PI*radius*radius;
}
...
}
Now we can create an array of elements whose type is Shape and store
either Circle or Square objects in that array. Whether the object is a
Circle or Square can, as before, be determined at execution time by using
the instanceof operator.
Exercises 8.4
1. For the points array discussed in this section, write a fragment that
would set all the values of the array to represent the complex number
1 + i. You may assume the existence of a constructor with header
public Complex (double re, double im).
2. (a) For the child development study discussed in this section, write
a method that could be added to the Child class that prints
the data (name, age, height, and sex) for a child on two lines
with the name on the first line and the other data on the second
line. Print the height in centimetres, rounded to the nearest
centimetre.
(b) Write a fragment that uses this method to print the data on all
the children.
(c) Write a method that would produce the same output as the
method shown in part (a) if we had chosen to use parallel arrays
rather than an array of objects to represent our data.
330 CHAPTER 8. ARRAYS
Up to now, we have assumed that arrays are always exactly the right size —
the number of elements is equal to the length of the array. Often, however,
we are faced with situations where the size of an array is not known in
advance. To handle such situations, we must modify our approach slightly.
A simple solution to the problem is to create an array whose length is
great enough to hold the maximum number of items that we will ever need
and then have another variable keep track of the current size of the array
— the number of cells that are currently occupied by data. Although it is
not necessary to do so, we think that it is a good idea to always identify
the current size in a consistent way. We will always identify these variables
using the array name followed by Size.
8.5. PARTIALLY FILLED ARRAYS 331
Example 1
Assuming that MAX_LIST_LENGTH has a value of 100, then the fragment
double[] list = new double[MAX_LIST_LENGTH];
int listSize = 0;
creates a double array called list whose maximum size is 100 (available
in the variable list.length) and whose current size is initialized to zero
(available in the variable listSize).
Example 2
The fragment shown here adds an item to a partially filled array called
list. If there is no room in list, the fragment doubles its size before
adding the new element.
if (listSize == list.length)
{
// array too small - double its size
double[] temp = new double [2*list.length];
for (int i = 0; i < list.length; i++)
temp[i] = list[i];
list = temp;
}
332 CHAPTER 8. ARRAYS
list[listSize] = item;
listSize++;
The diagrams show the effect of adding an element if the array is already
fully occupied. (Occupied cells are indicated by the shaded regions.)
-
-
A stack is a list whose size can grow and shrink by inserting or deleting
items at one end of the list. The end at which insertions and deletions are
made is called the top of the stack; the other end is called the bottom of the
stack. Stacks have a physical analogy in the spring-operated mechanisms
sometimes used to store stacks of plates in cafeterias. In these devices, if
you place a new plate on the top of the stack, the added pressure pushes
the stack down a bit; if you remove a plate from the top, the decreased
pressure allows the remaining plates to pop up a bit.
8.5. PARTIALLY FILLED ARRAYS 333
• pop delete and return the item at the top of the stack
In the class Stack that follows, when we create a new stack, we start
it with an array that will hold ten items. If the size ever proves to be
inadequate, we double the stack’s capacity. Notice that the array that is
used to hold the elements of the stack and its size field are both made
private. This ensures that a user cannot manipulate the array directly.
If we ever decide to change the implementation, it will not affect the way
that the user interacts with the class.
Example 3
The following class implements the concept of a stack using partially filled
arrays of type Object. An attempt to delete an item from an empty stack
(in the pop method) will cause an exception to be thrown. The exception
will print a message and execution of the program will terminate. For
information on exceptions, see Appendix E.
class Stack
{
private Object[] stack;
private int stackSize;
public Stack ()
{
// create a new, empty stack
stack = new Object[10];
stackSize = 0;
}
4 In Chapter 12, we will examine a very different way of implementing stacks.
334 CHAPTER 8. ARRAYS
Exercises 8.5
1. Complete the definition of the method get so that it returns the k th
element from a partially filled array of objects. If the array does not
have a k th element, the method should return null.
public static Object get (Object[] list,
int listSize, int k)
2. Write a fragment that prints the largest value in a partially filled array
of double values called scores. If the array is empty, the fragment
should print an appropriate message. Assume that the int variable
scoresSize contains the current size of the array.
3. Write a complete program that repeatedly prompts a user for positive
integers and reads those values until the user supplies a value of zero.
The program should then print the values read, in the order that they
were supplied by the user. The program should not place a restriction
on the number of values that can be read.
4. Another possible operation on stacks is usually called peek. It notes
and returns the value of the item currently at the top of the stack.
Write a method peek that could be added to the class Stack shown
in Example 3. The method should have header
public Object peek ()
If the stack is not empty, the method should return a reference to
the object at the top of the stack. Otherwise it should throw an
exception. The method should not alter the contents of the stack.
Avoiding Errors
1. By far the most common error in using arrays is allowing an index to
wander out of its defined range. Although this happens to everybody
sooner or later, you can reduce the chance of it happening to you by
(almost) always using for statements to process arrays. In setting
336 CHAPTER 8. ARRAYS
up such statements, use the array’s length to control the loop. For
example, if processing the elements of an array called list, write
for (int i = 0; i < list.length; i++) . . .
2. Even if an operation on an array has some conditional aspect, it is
still usually best to use a for statement rather than a while or a do.
As an example, suppose we have an array called list and we want
to set the variable negativeIndex to the index of the first negative
value in list (or −1 if there are no negative values in the array), we
could write
3. One sure way to avoid errors with arrays is not to use them. Although
this is often an unrealistic choice, there are many problems for which
a solution without arrays is both possible and simpler. Before auto-
matically creating an array, ask yourself if it is really necessary to do
so. For example, if we want to read a sequence of values and deter-
mine the largest, it is not necessary to use an array because we never
need all the values at once.
Debugging
3. As with all objects, there are two parts to the creation of an array:
declaration of a reference to the array and allocation of storage for
the array itself. For example, the sequence
int [] list;
list = new int[10];
first creates list, a reference to an int array and then creates an
array of int values (all initialized to 0) with the variable list acting
as a reference to the array. The two operations can be (and usually
are) combined into one statement
int [] list = new int[10];
but you should be clear about the two operations that are being
performed by this single statement. If you forget to allocate space
for the array, the compiler will catch you. For example, the fragment
double [] list;
list[0] = 1;
will cause the compiler to produce the message:
variable list may not have been initialized
Exercises 8.6
1. The following fragment determines the largest number in list, an
array of double values. Rewrite the fragment to make it clearer.
2. Suppose that you are required to read a set of values and determine
the given quantity. For which ones (if any) would you need to use an
array?
(a) the largest value (b) the median
(c) the mean (d) the range
int total = 0;
for (int i = 0; i < list.length; i++)
for (int j = 0; j < list.length; j++)
total += table[i][j];
2. State the value of the indicated element after execution of the given
declaration.
(a) a[4] after int[] a = new int[20];
(b) b[23] after boolean[] b = new boolean[50];
(c) c[2] after int[] c = {4,7,2,8};
(d) d[33] after double[][] d = new double[100][];
3. How much space (in bytes) would be required to store the elements
of the arrays created by the following declarations?
(a) double[] a = new double[10];
(b) byte[] b = new byte[30];
(c) int[][] c = new int[10][20];
(d) char[][][] d = new char[5][4][50];
4. Given that list is a one-dimensional array of int values, write frag-
ments to print each value.
(a) the number of occurrences of the value zero,
(b) the product of all the elements,
(c) the sum of the positive elements,
(d) the minimum value.
5. Given that table is a two-dimensional rectangular array, write frag-
ments to print each value.
(a) the number of elements
(b) the sum of the elements,
(c) the number of values that are multiples of three,
(d) the positive difference between the largest and smallest values
in the array.
6. A polynomial in x of degree n is an expression of the form
a0 + a1 x + a2 x 2 + · · · + an x n (an 6= 0)
1
2 2
3 4 3
4 7 7 4
5 11 14 11 5
Write a program that asks the user for a positive integer and, once
a satisfactory value has been supplied, produces that many rows of
Lacsap’s Triangle. For simplicity, do not try to print the triangle in
the symmetrical form shown here.
9. A matrix is a rectangular array of values. The transpose of a matrix
is the matrix obtained by interchanging the rows and columns of the
original matrix. As an example,
3 2
3 1 4
the transpose of is 1 0
2 0 7
4 7
Write a Java method called transpose that has one parameter, a two-
dimensional array of int values. The method should return a two-
dimensional array that is the transpose of the original array. Assume
that the parameter is a rectangular array.
10. A magic square of order n is a square array containing the integers
1, 2, . . . , n2 , each one used exactly once. The values in each row,
column, and diagonal must sum to the same value. As an example,
the following array is a magic square of order 3.
4 3 8
9 5 1
2 7 6
8.7. REVIEW EXERCISES 8 341
Write a boolean method isMagic that returns true if and only if its
single int[][] parameter represents a magic square.
Projects
2 3 4 5 6 7 8 9 10 11 12 13 14 15 ...
The first number in this list, 2, must be prime but all multiples of
two cannot be prime and hence can be eliminated from the list to
give us
2 3 /4 5 /6 7 /8 9 10
// 11 12
// 13 14
// 15 ...
The next number still in the list, 3, must also be prime since it was
not eliminated when we found multiples of two. We can, however,
eliminate all multiples of three to obtain
2 3 /4 5 \/6 7 /8 \9 10
// 11 12
//
\\ 13 14
// 1\5
\ ...
12. (a) The mode of a group of values is the value that occurs most
often in the group. A group may have more than one mode if
more than one value occurs with the maximum frequency. Such
a group is said to be multi-modal. (If there are exactly two
modes, the values are said to be bi-modal.) Write a program
that will read an unknown number of marks (out of 100) quitting
reading when a sentinel of −1 is read. The program should then
determine and print the mode(s) of the marks.
(b) Modify the program so that it also computes the median. If
there is an odd number of ordered values, the median is the
middle one; if there is an even number of values, the median is
the mean of the two scores adjacent to the middle. For example,
for scores of 51 68 68 73 84, the median is 68 while for scores of
51 68 68 73 84 90, the median is 70.5 (the mean of 68 and 73).
13. Write a program that first reads the positions of a number of pieces
on a chess board and then reads the location of another position. A
piece can attack a position if it can move into that position. Your
program should determine which of the pieces can attack the given
position.
Chess is played on an 8 ×8 board whose rows are numbered from
1 to 8 (from the bottom to the top) and whose columns are lettered
from a to h (from left to right). In this problem you are only going
to be dealing with the following pieces:
The program should read the position of a piece by reading the piece
code, the column, and the row in that order, one character at a time.
It should continue to read the positions of pieces until the piece code
is X, indicating the position to be attacked. Assume that all input is
valid.
8.7. REVIEW EXERCISES 8 343
8 ......XO
7 .....XXO
6 ........
5 ........
4 ........
3 ........
2 XX......
1 XX......
12345678
345
panies need to figure out how to take advantage of these resources and
integrate them effectively into their business models. For example, many
organizations are now outsourcing their customer support to call centres in
India – this has enormous potential but also creates new challenges around
customer service.
Q: How do you help an organization look at its IT Strategy? What are the
key elements you focus on?
A: First and foremost, you have to look at the business strategy and ensure
the IT strategy supports the prime goals and objectives of the company.
Second, I like to get an external point of view, looking for examples from
other industries of organizations that have made excellent use of a partic-
ular technology or approach. Third, I recommend that organizations tie
their IT strategy development cycle to the business planning cycle.
I like to look at IT systems from a portfolio perspective. Each project
needs to be constantly monitored and managed to ensure that it is still
supporting the business plans. Organizations often fall into the trap of
continuing to invest in projects that no longer meet core business require-
ments.
Q: What do you think about Java?
A: It is a very powerful programming language and the portability to mul-
tiple devices (i.e. “write once, use many”) is extremely valuable. I think
that the real power will come when organizations really embrace wireless
applications.
Q: What career advice do you have for someone studying computing science
today?
A: First, take advantage of opportunities to obtain business knowledge; the
better you understand business issues, the more effective you will be in any
IT career. Second, I recommend not getting too focused on a single tech-
nology because it will change. Third, try to get as much diverse experience
as possible, perhaps working for a big company for a while and then going
to a small software shop; you will learn valuable lessons from both kinds
of environments.
346
Chapter 9
Strings
Example 1
The statement
String friend = new String("Kate");
creates a new string as shown in the next diagram.
friend
- "Kate"
This constructor is rarely used; Java has an equivalent, simpler form that
can be used to create and initialize a variable of type String. The state-
ment
String friend = "Kate";
has exactly the same effect as the preceding statement and is easier to
write.
Example 2
The statements
String s1 = "Sample";
String s2 = new String(s1);
would create two String objects, both containing the value "Sample".
9.1. STRING OBJECTS 349
s1 s2 s3
.
- ""
1 Thereis a class StringBuffer in the java.lang package that does contain mutator
methods but we will not be using this class.
350 CHAPTER 9. STRINGS
Example 3
The fragment
String s = "Hello";
s = "Bonjour";
first sets the variable s to refer to a string object containing "Hello" and
then sets s to refer to a new string object containing "Bonjour" (at which
point the object containing "Hello" is lost). The result is illustrated in
the next diagram.
- "Bonjour" "Hello"
Exercises 9.1
1. What would be produced by a program containing the following frag-
ment?
String s;
System.out.println(s);
2. What would be printed by this fragment?
String s, t, u;
s = null;
t = "";
u = " ";
System.out.println("s is |" + s + "|");
System.out.println("t is |" + t + "|");
System.out.println("u is |" + u + "|");
Example 1
If we had made the declaration
String s = "Sergiy";
then the statement
System.out.println(s.length());
would print the value 6.
charAt
Another concept seen with both arrays and strings is that of an index.
Recall that, for an array, an index gave the position of an element in the
array, starting from zero. With strings, an index gives the position of a
character in the string, again starting from zero. There are a number of
methods in the String class that use an index. The simplest is charAt, an
instance method whose header has the form
public char charAt (int i)
The method returns the value of the character at index i in its implicit
String object. A value of i out of the range of the string will cause a
StringIndexOutOfBoundsException to be thrown.2
Example 2
Suppose that we have made the declaration
String s = "Jasper";
Then
(a) s.charAt(0) will return ’J’
indexOf
The method indexOf enables us to locate a particular character in
a string. The method is overloaded with the simplest version having the
following header:
public int indexOf (char c)
This method searches its implicit String object from left to right for an
occurrence of c. If c is a character in the string, the method returns the
index of the leftmost occurrence of c; if c does not occur in the string,
the method returns −1. The type of the argument passed to indexOf
is normally char but the method permits the use of an argument of any
integer type other than long.
Another version of indexOf is useful if we do not necessarily want the
leftmost occurrence of a character. This version has the header
public int indexOf (char c, int i)
This form of the method returns the index of the first occurrence of c that
has index at least equal to i. As before, it returns −1 if no such occurrence
exists.
Example 3
Given the declaration
String s = "Toronto";
then
(a) s.indexOf(’T’) will return 0
(b) s.indexOf(’t’) will return 5
(c) s.indexOf(’m’) will return −1
(d) s.indexOf(’o’,3) will return 3
(e) s.indexOf(’o’,4) will return 6
(f) s.indexOf(’r’,3) will return −1
To illustrate how these methods can be used, the next example shows
two ways that we could perform a simple task involving strings.
354 CHAPTER 9. STRINGS
Example 4
Both versions of the method printLocations print the indices in the string
s of the locations that contain the character c. The first method uses
charAt to examine every character while the second method uses indexOf
to jump to the locations in which the character occurs. In the second
method, the constant NOT_FOUND is set to −1, the value returned by index-
Of if it fails to find the specified character.
substring
Just as we can use charAt to extract characters from a string, we can
use substring to extract a part of a string from a string. The substring
method, like indexOf, is overloaded. Its simplest form has the header
public String substring (int start)
This method returns a String object, the substring of its implicit object
starting at the index specified by start and continuing to the end of the
string. If the value of start is less than zero or greater than the length of
the string, the method throws a StringIndexOutOfBoundsException.
9.2. STRING METHODS 355
Example 5
Given the declaration
String s = "Brian Auyeung";
then
(a) s.substring(3) will return "an Auyeung"
(b) s.substring(2,5) will return "ian"
(c) s.substring(20) will throw a StringIndexOutOfBoundsException
trim
Another method that is useful in manipulating strings containing text
is trim, an instance method in the String class that returns a string that
is identical to its implicit object but with any leading or trailing blanks
removed. It also removes any leading or trailing white space characters.
White space includes blanks and control characters such as tab (\t), new-
line (\n), carriage return (\r), and form feed (\f) characters. The method
does not alter the original, immutable string. Instead, it creates a copy
with the appropriate alterations.
Example 6
Given that
s = " Lots of extra blanks "
then
s.trim()
will return the string
356 CHAPTER 9. STRINGS
Example 7
The following method will create a string identical to its string parameter
but with all leading blanks, trailing blanks and instances of two or more
consecutive blanks removed. The method first uses trim to eliminate any
leading or trailing white space. It then uses indexOf to locate remaining
blanks. For each blank found by indexOf, charAt is used to see if the ad-
jacent character is also a blank and, if it is, substring is used to eliminate
the second blank. Once all excess blanks have been removed, the method
returns the compressed string.
public static String removeSpaces (String oldString)
{
final int NOT_FOUND = -1;
String newString = oldString.trim();
int nextBlank = newString.indexOf(’ ’);
while (nextBlank != NOT_FOUND)
{
while (newString.charAt(nextBlank+1) == ’ ’)
newString = newString.substring(0,nextBlank+1)
+ newString.substring(nextBlank+2);
nextBlank = newString.indexOf(’ ’,nextBlank+1);
}
return newString;
}
To use this method to eliminate excess blanks in myString, we could write
myString = removeSpaces(myString);
9.2. STRING METHODS 357
Example 8
Given the declaration
String s = "Bart & Lisa";
then
(a) s.toLowerCase() will return "bart & lisa"
(b) s.toUpperCase() will return "BART & LISA"
(c) s.equalsIgnoreCase("BART & lisa") will return true
valueOf
Values of any type can be converted to strings. To convert an ob-
ject to a string, we can use the object’s toString method. To convert
a value in any of Java’s primitive types to a string, we can use the class
358 CHAPTER 9. STRINGS
method valueOf in the String class. The method is overloaded so that its
parameter can be of any primitive type.
Example 9
(a) String.valueOf(123) returns "123"
(b) String.valueOf(’a’) returns "a"
(c) String.valueOf(2.5) returns "2.5"
Example 10
If s has the value "abc", then the expression s.toUpperCase().charAt(1)
will be evaluated as follows:
s.toUpperCase() returns the value "ABC".
"ABC".charAt(1) then returns the value ’B’.
Note that the operations do not change the value of the string s.
Exercises 9.2
1. Given the declaration
String s = "Amanda Chui";
find the value of each expression.
9.2. STRING METHODS 359
(a) String.valueOf(2*4)
(b) String.valueOf(27%7)
(c) String.valueOf((char)(’A’ + 4))
(d) String.valueOf(3 < 4 && 5 < 6)
3. Assuming that the string name has the value "Avi Laurie", find the
value of each expression.
(a) name.toLowerCase().indexOf(’a’)
(b) name.toUpperCase().charAt(5)
(c) name.substring(3).indexOf(’i’)
(d) name.substring(2).toUpperCase()
(e) name.toUpperCase().indexOf(’A’,1)
(f) name.substring(name.indexOf(’ ’)+1).length()
seasons
?
seasons[0] - "Spring"
seasons[1] - "Summer"
seasons[2] - "Fall"
seasons[3] - "Winter"
All of this could have been accomplished in one step by using the array
initializer notation that we have seen many times before. To create and
initialize the seasons array, we could write
String[] seasons = {"Spring","Summer","Fall","Winter"};
You may have noticed that we have already seen string arrays many
times. In fact, you have been using them (probably without realizing it)
since your very first program! Every main method in Java starts with a
header of the form
public static void main (String[] args)
The parameter here is a String array called args. Parameters in Java are
given the values of their arguments when a method is called. Since the main
method is not called from another method, values are given to arguments of
the main method somewhat differently than for other methods. To be able
to pass argument values to main, you must be able to use a command line
interface to run a program. If you are used to running your Java programs
in an integrated development environment (IDE), it may be possible to do
so from within the IDE but you may have to open a command window. You
should check the documentation of your IDE or speak with your system
administrator for the details.
Using a command line, we call the main method by writing the word
java followed by the name of the class that contains main. If we want
to give arguments to main, we can do so simply by writing them on the
same line that we use to make the call. Because of this, these arguments are
known as command line arguments. They are automatically assigned to the
string array parameter of main (usually called args). The first argument
is assigned to args[0], the second to args[1], and so on. Any command
362 CHAPTER 9. STRINGS
line arguments are separated from each other by at least one blank. It is
not necessary to surround each argument by double quotes unless we want
an argument to contain blanks.
Example 1
A program containing a main method in a class Sample could be called by
the command
java Sample George John Paul Ringo "The Beatles"
This would create an argument array of length five. If the parameter of
the main method were called args, then we would have
args[0] = "George"
.
.
args[4] = "The Beatles"
The quotes around The Beatles (on the command line) ensure that those
two words are considered as one string. Without the quotes, we would have
args[4] = "The"
and
args[5] = "Beatles"
Command line arguments can represent any type but they are always
stored as strings and, if we want to use them for other purposes, we must
convert them appropriately. Often, there are methods available to assist
us in these conversions. For example, if we want to convert a string to an
integer, we can use parseInt, a class method in the Integer class having
the header
public static int parseInt (String s)
The method returns the integer represented by s. If s does not represent
an integer, the method throws a NumberFormatException.
Example 2
Suppose a program contains a main method in a class Update. The program
expects a command line giving the date in the form <year> <month>
<day>. We could invoke the program by writing
9.3. ARRAYS OF STRINGS 363
Example 3
The following fragment could be used in a main method to check that a
user has supplied the three arguments required in the command line.
Exercises 9.3
1. Complete the definition of the method avLength so that it returns
the average length of the strings in its array parameter.
public static double avLength (String[] list)
provides a number of methods for dealing with strings that are composed
of sequences of tokens, separated by delimiters. The tokens are usually
words and the delimiters are usually blanks. As an example, consider the
following string:
String quote = "I can resist anything except temptation";
If we consider blanks to be delimiters, then this string contains six tokens:
"I", "can", . . . , and "temptation".
We can use the StringTokenizer class to extract the tokens from such
a string, as illustrated in the following example.
Example 1
The following fragment prints the words of the string quote with one word
per line. It does so by first creating a new StringTokenizer object and
then, as long as the hasMoreTokens method indicates that there are still
more words to be processed, it uses the nextToken method to extract the
next word.
StringTokenizer st = new StringTokenizer(quote);
while (st.hasMoreTokens())
System.out.println(st.nextToken());
The output from the fragment would be
I
can
resist
anything
except
temptation
StringTokenizer Constructors
The class has three constructors, each of which has one String parameter
that is used to create a StringTokenizer object. They differ in their
treatment of delimiters.
• public StringTokenizer (String s)
This constructor, as we showed in Example 1, creates a String-
Tokenizer object for the string s. As we noted there, it uses blanks
366 CHAPTER 9. STRINGS
Example 2
(a) The statement
StringTokenizer st1 = new StringTokenizer
("12*(345+6789)","*/+-()",true);
would create a StringTokenizer object st1 using the given string.
The delimiters are any of the characters in the string "*/+-()". Be-
cause the value of the third argument is true, the delimiters are
treated as tokens. The resulting tokens of st1 are
"12"
"*"
"("
"345"
"+"
"6789"
")"
StringTokenizer Methods
We have already seen two methods of the class in Example 1. Here we
will take a closer look at those plus one other. Collectively, the methods
allow us to scan through a string from left to right, one token at a time,
extracting tokens as we proceed.
• public String nextToken ()
This returns the next token from the implicit string tokenizer object.
The first time that this method is called, it returns the leftmost token
in the string. Each call causes it to move along the string to the
position of the next token. If there are no more tokens, the method
throws a NoSuchElementException.
• public boolean hasMoreTokens ()
This method checks to see if there are any more tokens available
from the implicit string tokenizer object. It returns true if and only
if there is at least one token in the string after the current position.
• public int countTokens ()
This method returns the number of tokens remaining in the implicit
string tokenizer object.
368 CHAPTER 9. STRINGS
Almost all the classes that we have been working with up to now have
been in the core package called java.lang. The class StringTokenizer,
on the other hand, is in the package java.util. If a program is to use a
method from a class outside java.lang, we must inform the compiler of
this. One way to do so is to use an import statement at the beginning of
a program. If we want to use the StringTokenizer class in a program, we
can precede the program with the statement
import java.util.StringTokenizer;
As an alternative, we can write the import statement in the form
import java.util.*;
This allows us to use any method in any of the classes in the java.util
package. This form is useful if more than one class in a package is required
in a program.
Exercises 9.4
3. Write a class method value that will return, as an int, the value of a
simple arithmetic expression contained in a string. The string should
contain two integers surrounding one of the operators *, /, %, +, or
-. The string may also contain blanks. As an example, given that
s = " 13 + 2"
the method should return the value 15. The method should assume
that its argument contains a valid expression.
9.5. AVOIDING ERRORS AND DEBUGGING 369
Avoiding Errors
1. Although we have mentioned it previously, it may bear repeating that
a declaration like
String s;
only creates a variable that is capable of acting as a reference to a
string. It does not create such a string and, in fact, it does not
initialize s to any value at all. If, on the other hand, we write
String t = null;
then t will be initialized although it does not refer to a string. In
this state, t can be printed (producing the word null) or compared
to other string references. As a third possibility, if we write
String u = "";
then u is a reference to a string containing no characters. With the
three declarations shown here, an attempt to compare s to either t
or u will produce a compilation error while the expression t == u has
the value false.
3. The fact that strings are immutable objects can often cause problems.
Once a string object has been created, it cannot be altered. This
implies that methods that have string parameters cannot alter them
in any way. On the other hand, if we want to change the value of
a string reference, we can do so by assigning it a new value. For
example, the statement
s = s.toUpperCase();
first evaluates the expression s.toUpperCase() to produce a new
string which is a copy of s but with all lower case letters converted to
370 CHAPTER 9. STRINGS
c s
’*’
- "*"
We can convert from one type to another but this must be done with
care. We cannot use a simple assignment or even an assignment with
a cast. If we wanted to assign the value of a one-character string s
to a character c, we could write
c = s.charAt(0);
On the other hand, if we wanted to assign a character c to a string
s, we could write
s = String.valueOf(c);
Debugging
Exercises 9.5
1. Suppose that we are given the declarations
String s;
String t = null;
String u = "";
String v = " ";
State, with reasons, what would occur if the program containing these
declarations attempted to print the value of each expression.
(a) s.length() (b) t.length()
(c) u.length() (d) v.length()
3. Using the table on page 600 where necessary, determine what would
be printed by each statement.
(a) System.out.println("" + ’$’ + ’2’);
(b) System.out.println(’$’ + ’2’);
(c) System.out.println(’$’ + 2);
(d) System.out.println("$" + ’2’);
(e) System.out.println("$" + 2);
(f) System.out.println(’$’ + ’2’ + ".00");
4. Write a method whose heading is
public static String changeFirst (String s,
char oldChar, char newChar)
The method should return a string in which the leftmost occurrence
of oldChar in s is replaced by newChar. If oldChar does not appear
in s, the method should simply return s.
String s = "one";
String t = new String(s);
String u = s;
u = "two";
t = "three";
System.out.println(s + " " + t + " " + u);
5. Suppose that we want a class Dog in which each dog has a name, a
breed, and an age (in years). The fields of the class are
(a) Write an equals method for the Dog class. Two Dog objects
should be considered equal if the breeds are the same and the
ages differ by one year or less.
9.6. REVIEW EXERCISES 9 375
6. Write a program that reads a string and then prints a diamond shaped
array based on the characters of the string. As an example, if the
string has the value "SAMPLE", then the program should print the
pattern
S
SAS
SAMAS
SAMPMAS
SAMPLPMAS
SAMPLELPMAS
SAMPLPMAS
SAMPMAS
SAMAS
SAS
S
7. A palindrome is a string that reads the same both forward and back-
ward. Examples of palindromes are “radar”, “31413”, and “god a
dog”.
Projects
9. Write a program that reads names in standard form and prints them
in the form
The program should prompt the user for names, halting when the
user provides the name ZZZ. As examples, input of
Santa Claus
Michael J. Fox
Madonna
William Henry Richard Charles Windsor
ZZZ
should produce output of
Claus, S.
Fox, M. J.
Madonna
Windsor, W. H. R. C.
10. Write a program that reads a string containing a Roman numeral
representing a value in the range 1 to 3999. The program should
print the Roman numeral and its value in our notation (a Hindu-
Arabic numeral).
• Roman numerals use the symbols I = 1, V = 5, X = 10, L = 50,
C = 100, D = 500, and M = 1000.
• Roman numerals use an additive rather than a positional nota-
tion. Numerals are formed by writing symbols from the preced-
ing list, from left to right, to represent a sum, each time using
the symbol for the largest possible value from the list of sym-
bols. This rule is subject to the limitation that no symbol may
9.6. REVIEW EXERCISES 9 377
aVeryVeryLongWordThatMayNotFitOnOneLine.
The last line
ZZZ
Reasonable people adapt to the
world; the unreasonable ones
persist in trying to adapt the
world to themselves. Therefore all
progress depends on unreasonable
people.
aVeryVeryLongWordThatMayNotFitOnOneLine.
aVeryVeryLongWordThatMayNotFitOnOneLine.
To begin, suppose that you are looking for a particular CD in your collec-
tion. After rummaging around for a while with no luck, you may decide
to go through the collection systematically, looking at each CD in turn. If
you are patient, you will eventually find the one that you are looking for
(if it is there).
This simple technique, called a sequential search, can be applied to an
array of values.
Example 1
The following method performs a sequential search on an array of String
values for the value called item. If the search is successful, the method
returns the index in the array of item but, if the search fails, the method
returns −1.
public static int seqSearch (String[] list, String item)
{
int location = -1;
for (int i = 0; i < list.length; i++)
if (list[i].equals(item))
location = i;
return location;
}
Note that the variable location is initialized to indicate that the search
is unsuccessful. If item is not found, this is the value that the method will
return. Only if we find item in the list will location be changed.
Example 2
This method shows an efficient implementation of sequential search that
quits examining a list immediately after the search has found item.
public static int seqSearch (String[] list, String item)
{
int location = -1;
boolean found = false;
for (int i = 0; i < list.length && !found; i++)
if (list[i].equals(item))
{
location = i;
found = true;
}
return location;
}
Example 3
By eliminating the boolean flag found and the corresponding test in the
for statement to determine the state of found, we can create the following
very efficient sequential search.
public static int seqSearch (String[] list, String item)
{
for (int i = 0; i < list.length; i++)
if (list[i].equals(item))
return i;
return -1;
}
Now, if a match for item is ever found, we exit both the for statement and
the method immediately. Only if no match is ever found do we complete
the for statement and return −1.
384 CHAPTER 10. SEARCHING AND SORTING
Although the code of Example 3 is both simpler and more efficient than
that of Example 2, some people still argue that the solution in Example 2
is preferable. In Example 3, the first line of the for statement would make
a reader think that the loop is going to be executed for every value in the
array but, if a match for item is found, this does not happen. In Example
2, on the other hand, the first line of the for statement is quite clear about
what is going to happen — the loop will be executed as long as we haven’t
reached the end of the array and we haven’t found what we are seeking.
Exercises 10.1
1. Suppose that the method of Example 1 was used to search an array.
What would the method return if item appeared in the list more than
once?
2. What changes should be made to the sequential search in Example 2
so that it searches an array of values starting at the top and moving
downward?
3. A modification of the basic sequential search operates in the following
way. If the item being sought is found, it is interchanged with the
item that preceded it. If, for example, we were searching for 7 in the
list
3 9 5 7 2 8 4
then, after finding 7, the list would be rearranged in the order
3 9 7 5 2 8 4
(a) Write a method that implements this technique to search an
array of int values.
(b) Test your method in a program that first asks for the length
of the list to be searched and then reads that many integers
into an array. The program should then repeatedly prompt the
user for values until the user supplies a sentinel of zero. The
program should print the initial list and then, for each non-zero
value read, it should use your modified sequential search to try
to locate that item and then print the resulting array.
(c) Why might this modification sometimes improve the efficiency
of a sequential search?
10.2. BINARY SEARCH 385
Suppose that you are, once again, looking for a CD in your collection. If
you have gone to the trouble of putting the CD’s in order, your search
can be speeded up considerably and you should be able to locate the one
that you want very quickly. Our next algorithm, called a binary search,
provides an efficient method for searching a list in which the items are
ordered. The algorithm is an example of a large class called divide and
conquer algorithms. These algorithms employ strategies that solve the
problem by quickly reducing its size. For a binary search, at each stage of
the solution, we cut the size of the problem roughly in half.
To illustrate the way that the algorithm works, suppose that we are
searching for the item 47 in the sorted list shown below.
16 19 22 24 27 29 37 40 43 44 47 52 56 60 64 71
To start the process, we initially examine the item in the middle of the
array. The middle item, 40, is not the one that we want but, because it is
less than the value that we are looking for and because the list is sorted, we
know that we can eliminate all the items in the lower half of the list. Our
search can now continue looking only at the remaining half of the original
list, as shown.
43 44 47 52 56 60 64 71
We now repeat our strategy on these items. Since there are now eight
items left, there is no exact middle; we can choose either 52 or 56. If
we look at 52, we see that it is larger than the value for which we are
searching. Again taking advantage of the fact that the items are ordered,
we can eliminate 52 and all items above it from our search. This leaves us
with only the following values.
43 44 47
We again look at the middle item, this time finding 44, which is too
small. We can therefore discard both it and the item below it, leaving us
with only one value.
47
386 CHAPTER 10. SEARCHING AND SORTING
Example 1
Suppose that we want to perform a binary search for the value 75 on the
following data.
12 34 47 62 75 87 90
Initially, we want to examine the entire array so the variables bottom and
top are initialized to 0 and 6, the indices of the first and last elements,
respectively while middle is set to (0 + 6)/2 = 3. (In the diagrams, we
use arrows to emphasize the purpose of bottom, middle, and top; they
should not be confused with the arrows that we use for reference variables.)
bottom middle top
0 3 6
? ? ?
12 34 47 62 75 87 90
0 1 2 3 4 5 6
Since 62 < 75, the item that we are seeking cannot be in the left half of
the list. We discard this half of the list by setting bottom to middle + 1.
10.2. BINARY SEARCH 387
? ? ?
12 34 47 62 75 87 90
0 1 2 3 4 5 6
Since 87 > 75, the value 75 cannot be in the upper half of this sublist so
we discard it by setting top to middle - 1. Since bottom and top are now
both equal to 4, the value of middle will be (4 + 4)/2 = 4.
???
12 34 47 62 75 87 90
0 1 2 3 4 5 6
Once middle has found the value, the search ends successfully.
The next example implements a binary search for the value item in an
array list of double values. It returns the index in list of item (if item
is in list) and −1 otherwise. Notice how the search terminates if the item
that we are looking for is not in the list. We saw in the last example how
the values of bottom and top converge as the search proceeds with bottom
getting larger and top getting smaller. If the item that we are seeking is
not in the list, eventually we will either set bottom to a value larger than
top or we will set top to a value smaller than bottom. Thus, we only keep
searching as long as bottom ≤ top and the item that we are seeking has
not been found.
388 CHAPTER 10. SEARCHING AND SORTING
Example 2
Exercises 10.2
1. Suppose that an array contains the following elements.
23 27 30 34 41 49 51 55 57 60 67 72 78 83 96
To use a binary search, we noted that it was necessary to have the items
sorted. We now turn our attention to finding ways of sorting data.
To develop algorithms of any kind, it is often useful to examine the
ways that we solve similar problems in everyday life. To develop a solution
to the problem of sorting data, let us look at how we might proceed if we
were playing a card game and we wanted to put our hand in order, from
smallest to largest. A common way of sorting such a hand is to examine
cards from left to right, rearranging them if necessary by placing cards
where they belong in the sequence of cards on their left. The next example
illustrates the process.
Example 1
Suppose that we have the five cards shown below (ignoring suits).
6 3 5 8 2
390 CHAPTER 10. SEARCHING AND SORTING
We could begin to sort these cards by looking at the 3, the second card
from the left. Since 3 < 6, the 3 should be inserted to the left of the 6.
This will produce the following arrangement in which the two values on the
left are in order.
3 6 5 8 2
The next card from the left, the 5, should be inserted between the 3 and
the 6. Doing this gives us the next arrangement in which the first three
cards are guaranteed to be in order.
3 5 6 8 2
Now we examine the 8. Because it is greater than any of the values to its
left, it should stay where it is and still give us the four leftmost values in
order.
3 5 6 8 2
Finally, looking at the 2, we can see that it must be inserted before any of
the other values. Doing this gives us our final, ordered arrangement.
2 3 5 6 8
Example 2
The method insertSort uses an insertion sort to arrange an array of
double values in ascending order.
public static void insertSort (double[] list)
{
for (int top = 1; top < list.length; top++)
{
double item = list[top];
int i = top;
while (i > 0 && item < list[i-1])
{
list[i] = list[i-1];
i--;
}
list[i] = item;
}
}
Exercises 10.3
1. An insertion sort is to be used to put the values
6 2 8 3 1 7 4
392 CHAPTER 10. SEARCHING AND SORTING
in ascending order. Show the values as they would appear after each
pass of the sort.
2. What changes would have to be made to the insertSort method in
Example 2 in order to sort the values in descending order?
3. What might happen if, in Example 2, the while statement’s first line
were written in the following form?
while (item < list[i-1] && i > 0)
4. Write a program that initializes an array with the names of the plan-
ets ordered by their distances from the sun (Mercury, Venus, Earth,
Mars, Jupiter, Saturn, Uranus, Neptune, and Pluto) and prints them
in that order on one line. The program should then use an insertion
sort to arrange the names alphabetically. To trace the progress of the
sort, have it print the list after each pass.
5. The median of an ordered list of numerical values is defined in the
following way. If the number of values is odd, the median is the middle
value. If the number of values is even, the median is the average of
the two middle values. Write a program that first prompts the user
for the number of items to be processed, reads that many real values,
and then finds their median.
6. A sort is said to be stable if it always leaves values that are considered
to be equal in the same order after the sort. Is the insertion sort
stable? Justify your answer.
Example 1
To begin sorting the data
7 1 9 3 5 4
we find the largest element, 9, and swap it with 4, the element at the right
end of the list. The result, after the first pass of a selection sort is
7 1 4 3 5 9
I
On the second pass, all the items except the last are examined to see
which of these is the largest and this item is then placed at the right end of
this sublist. This pattern continues on subsequent passes; on each one, the
largest value among the unsorted items is placed at the top of the sublist.
Example 2
Using the set of data shown in Example 1, the table shows the results of
successive passes of selection sort. The vertical bars indicate the division
points between the unsorted items and the sorted items.
After Pass
1 7 1 4 3 5 | 9
2 5 1 4 3 | 7 9
3 3 1 4 | 5 7 9
4 3 1 | 4 5 7 9
5 | 1 3 4 5 7 9
Notice that only five passes are required to sort six items. Once all but one
of the items are placed in their correct positions, the remaining item must
also be in its correct position.
To code this algorithm in Java, we note that, for an array called list,
we must successively find the largest item in sublists of sizes list.length,
list.length-1, . . . , 2. Since the upper bound of an array has index that
is one less than the size of the array, the loop that controls the passes of
the sort will have the form
394 CHAPTER 10. SEARCHING AND SORTING
Example 3
The method selectSort uses a selection sort to arrange an array of double
values in ascending order.
public static void selectSort (double[] list)
{
for (int top = list.length - 1; top > 0; top--)
{
int largeLoc = 0;
for (int i = 1; i <= top; i++)
if (list[i] > list[largeLoc])
largeLoc = i;
Exercises 10.4
1. If a selection sort were to be used to sort the data shown below in
alphabetical order, show the data after each pass of the sort.
4. On each pass of our version of selection sort, the largest value among
the remaining unsorted items was placed in its correct position. An
alternate form of the algorithm uses each pass to place the smallest
value among the remaining unsorted values in its correct position.
Our next (but not our last) sorting algorithm, called a bubble sort, is an
example of a class of sorts known as exchange sorts. With the bubble sort,
the basic idea is to compare adjacent values and exchange them if they are
not in order.
396 CHAPTER 10. SEARCHING AND SORTING
Example 1
Suppose that we want to use a bubble sort to arrange the names
Phil Ivan Sara Jack Gina
Next we compare Phil to Sara. Since they are in order, we leave them that
way giving
Now, comparing Sara to Jack, we see that they must be exchanged. Doing
this gives
The result of all this comparing and exchanging is that, after one pass, the
largest value (Sara, in our example) will be at the upper end of the list.
Like a bubble rising in a liquid, the largest value has risen to the top of the
list.
As with the selection sort shown in the previous section, we now repeat
the process used in the first pass on all but the last element. As we proceed,
the passes deal with shorter and shorter subsequences of the original list
until all values are in their correct positions.
Example 2
The following tables show the actions of a bubble sort in ordering the names
The colons indicate the values currently being compared. The vertical bars
indicate the division points between the unsorted data and the sorted data.
Phil : Ivan Sara Jack Gina
Ivan Phil : Sara Jack Gina
First Pass Ivan Phil Sara : Jack Gina
Ivan Phil Jack Sara : Gina
Ivan Phil Jack Gina | Sara
As we saw with selection sort, the number of passes needed to sort the
items is one less than the number of items. After all but one of the items
have been ordered, the remaining one must also be in its correct position.
To create a Java method for a bubble sort is fairly easy if we use some
of our previous techniques. To perform all the passes we need a loop like
that of the selection sort.
Within each pass, the code for comparing and exchanging values in a
sublist is easily written.
list[i] = list[i+1];
list[i+1] = temp;
}
Example 3
This method uses a bubble sort to place an array of strings in ascending
order. It uses a boolean flag, sorted, to make the bubble sort more efficient
by stopping the sort after a full pass in which there are no exchanges.
Exercises 10.5
1. Make tables like those shown in Example 2 to show the comparisons
and exchanges that would take place in using a bubble sort to put
the following data in ascending order.
3 8 3 2 7 5
2 9 4 6 1 7
400 CHAPTER 10. SEARCHING AND SORTING
10.6 Shellsort
Although insertion sort works well for short lists, it tends to slow down
drastically with long lists. This is because of the large numbers of compar-
isons that are needed to find the correct positions for insertions of data.
If, however, the elements of a list are almost in order, insertion sort is
very fast, even for long lists because in this case only a few comparisons
are needed to find the correct insertion point for each value. Shellsort, in-
vented by Donald Shell in 1959, adapts insertion sort to produce a sorting
technique that works well even with long lists that are randomly ordered.
With Shell’s adaptation of insertion sort, data can be moved long distances
with only a few comparisons.
The sort uses the following idea. We say that a list is k-sorted if,
starting at any point in the list, every k th element is in order. A k-sorted
list consists of k interleaved, sorted sublists. To illustrate the idea of a
k-sorted list, let us see how a list can be 3-sorted.
Example 1
Suppose that we are given an unsorted list of integers.
34 21 40 12 27 18 29 13 25 17 11 38
This can be decomposed into three sublists with the first list containing
the elements at positions 0, 3, 6, . . ., the second containing the elements
at positions 1, 4, 7, . . ., and the third containing the elements at positions
2, 5, 8, . . . .
34 12 29 17
21 27 13 11
40 18 25 38
10.6. SHELLSORT 401
12 11 18 17 13 25 29 21 38 34 27 40
Without doing very much work to obtain the 3-sorted list, we have gone a
long way toward our final goal.
Shellsort repeatedly uses insertion sort to create sorted sublists. Ini-
tially a k-sorted list is created using a large value of k so that values can
be moved long distances. On subsequent passes, other k-sorted lists are
formed using successively smaller values of k. On the final pass, the value
of k is set to one; a 1-sorted list is completely sorted.
Example 2
The tables show the results of using Shellsort on the values
64 31 10 40 22 49 82 20 29 56 40 18 19 27 26
in which successive passes of the sort creates lists that are 7-sorted, 3-sorted,
and 1-sorted.
Unsorted: 64 31 10 40 22 49 82 20 29 56 40 18 19 27 26
20 26 64
29 31
10 56
402 CHAPTER 10. SEARCHING AND SORTING
First Pass 40 40
18 22
19 49
27 82
7-Sorted: 20 29 10 40 18 19 27 26 31 56 40 22 49 82 64
20 27 40 49 56
Second Pass 18 26 29 40 82
10 19 22 31 64
3-Sorted: 20 18 10 27 26 19 40 29 22 49 40 31 56 82 64
Third Pass
1-Sorted: 10 18 19 20 22 26 27 29 31 40 40 49 56 64 82
In using Shellsort, we noted that the first pass should use a large
value of k so that values that had to be moved long distances could do
so quickly. We also noted that the last pass must use k = 1 in order to
guarantee a completely sorted list. It is natural to ask: what is the best
sequence of values of k? Surprisingly, the answer to that question is not
known, despite the fact that a good deal of work has been done on the
problem. One sequence, however, that has been found to give good results
is . . . , 40, 13, 4, 1. Working from the right end of this sequence, each term
is one more than three times as large as the term on its right.
Exercises 10.6
1. Given the following data, show how they would appear after they
have been 5-sorted.
26 37 21 41 63 19 61 72 55 29 47 18 26 22
2. Starting with the same set of data given in Question 1, show how
they would appear after they had been 4-sorted.
4. How would you answer the following argument against using Shell-
sort? “The last step of Shellsort, using k = 1, is simply a normal
insertion sort. Since Shellsort performs many preliminary steps be-
fore this final one, it must be slower than a single insertion sort.”
5. Suppose that you were going to write a version of Shellsort using
the sequence of k-sorts suggested in the text. For a list containing n
elements, the first value of k that should be used is the largest value
in the sequence that is less than n.
(a) Write a sequence of statements that will initialize k correctly for
a given value of n.
(b) Write a statement that will, for any value of k in the sequence,
produce the next smaller value of k.
6. (a) Write a method shellSort to sort an array of int values in
ascending order. In performing the k-sorts, use the sequence of
values of k suggested in the text. Be sure to use insertion sort
at each stage of the sort.
(b) Test your method by writing a complete program that first gen-
erates an array of 500 random int values in the range [0, 999],
prints this array (ten values per line), sorts the array using your
shellSort method, and then prints the resulting array (ten val-
ues per line).
7. Experiment with using Shellsort with sequences for k other than the
one given in the text, testing your sequences on large arrays of integers
(generated as indicated in the previous question) and noting the time
required by the sort in each case. To measure the time taken by a
sort, you can use the method currentTimeMillis in the System
class. The method has the signature
public static long currentTimeMillis()
It returns, as a long value, the number of milliseconds since midnight
GMT on January 1, 1970. To use the method, you might write
long startTime = System.currentTimeMillis();
just before starting the sort and
int elapsedTime =
(int)(System.currentTimeMillis() - startTime);
just after completing the sort. In choosing a sequence, remember
that the last value of k must be one but, beyond this, there are no
restrictions on the sequences. Can you find a sequence that performs
404 CHAPTER 10. SEARCHING AND SORTING
better than the one given in the text? (It is possible to beat the given
sequence; we chose it because it is fairly simple and reliable.)
In this chapter we have looked at two searching algorithms and four sorting
algorithms. We now turn our attention to the problem of comparing these
algorithms, to try to decide which is “best”.
Before we start comparing the efficiency of algorithms, we must agree
on some way of measuring efficiency. We do not want a measure that
is dependent on the language in which the algorithm is coded or on the
machine on which the program is executed. Instead, we want a measure
of performance that depends only on the “size” of the problem. For most
problems, there is usually some clear measure of the size. For searching
and sorting lists of values, the size of a problem is usually taken to be the
number of elements in the list.
Once we have a measure of the size of a problem, we then want to
determine how a solution to a problem grows relative to the growth in its
size. To measure growth of a solution, we often compare size to either the
space required by the solution or the time required by the solution. (Fre-
quently it turns out that there is a tradeoff between these two quantities;
an algorithm that is relatively fast will use more space than one that is
slower.) In this text, we will not be concerned with space considerations;
we will restrict ourselves to a (somewhat informal) study of growth in time
relative to the growth in size of our problems.
To begin, consider the sequential search algorithm developed in the
first section of this chapter. Most of the time spent by the search is devoted
to comparisons between the item sought and elements of the array being
searched. If there are n elements in the array, then the maximum number
of comparisons that would be required is n. If, on one machine, a single
comparison takes c1 time units, then, since most of the time taken by
execution of the algorithm is devoted to comparisons, the total time for
the search will be roughly c1 × n time units. If we let this time be T1 time
units, then we have
T1 =˙ c1 n
If we change computers or programming languages, we might find that a
single comparison now takes c2 time units. In this case, the total time, T2 ,
10.7. COMPARING ALGORITHMS 405
will be given by
T2 =
˙ c2 n
In either case, the amount of time required by the sequential search is
approximately proportional to n. If we let the time be T , then we could
write this approximate proportionality in the form
T ∝
˙ n
Instead of this notation, however, we will usually write the relationship in
the form
T is O(n)
Reasonably enough, the use of O is called big oh notation. We can read
the statement as “T is big oh of n” or “T is order n”
We should confess that we are, in fact, not really telling the truth
about big oh notation. More advanced courses, dealing with analysis of
algorithms, give a more precise definition.1 For our purposes, however, we
can think of the following relationships for a function f as being equivalent.
T =
˙ cf(n)
T ∝˙ f(n)
T is O(f(n))
In making our analysis of the time required by a sequential search, we
supposed that all the elements of a list would have to be examined. This is
known as a worst case analysis. If we wanted to examine the average time
required by a sequential search, we might make the assumption that the
data are randomly ordered so that, on average, we would have to examine
about half of the elements to terminate a successful search. If the worst
case gave
T = ˙ cn
then the average case (under our assumptions) would be
1
T =
˙ cn
2
This means that T is still approximately proportional to n. Using our big
oh notation, we have, once again,
T is O(n)
1 Here is
such a definition: A function g(n) is said to be O(f (n)) if there exist constants
c > 0 and n0 , a positive integer, such that g(n) ≤ cf (n) for all n ≥ n0 .
406 CHAPTER 10. SEARCHING AND SORTING
cn =
˙ 1 + cn/2
Similarly
cn/2 =
˙ 1 + cn/4
Substituting the second equation in the first gives
cn =
˙ 2 + cn/4 = 2 + cn/22
cn =
˙ 3 + cn/8 = 3 + cn/23
cn =
˙ k + cn/2k
˙ k + 1 where n/2k = 1
cn =
cn = lg n + 1
cn =
˙ lg n
Since the time required by the search will be roughly proportional to the
number of comparisons, we have, finally
T ∝˙ lg n
T is O(lg n)
(n − 1) + (n − 2) + (n − 3) + · · · + 1
and we know from mathematics that the sum of this arithmetic series is
1 2
(n − n)
2
As with our searches, most of the time used by selection sort is spent doing
comparisons. If we let T units be the total time taken by selection sort,
then
1
T ∝˙ (n2 − n)
2
Since we are dealing here with proportionalities, we can eliminate the con-
stant multiplier to obtain
˙ (n2 − n)
T ∝
or
T is O(n2 − n)
n n2 − n n2 (n2 − n)/n2
1 0 1 0.0
10 90 100 0.9
100 9 900 10 000 0.99
1 000 999 000 1 000 000 0.999
10 000 99 990 000 100 000 000 0.9999
100 000 9 999 900 000 10 000 000 000 0.99999
1 000 000 999 999 000 000 1 000 000 000 000 0.999999
As you can see from the table, as n gets larger, the effect of subtracting n
from n2 becomes (in relative terms) almost insignificant. For large values
of n, the expressions n2 − n and n2 are relatively very close as indicated
by the ratios in the last column. In such a situation, where one term
of an expression dominates any others, our big oh notation allows us to
simplify our description of an algorithm’s behaviour by eliminating the
non-dominant terms. For the selection sort we can, therefore, write
T is O(n2 )
In addition to comparing items, sorting also involves moving them
around if they are out of order. Using a selection sort on a list with n
items, we may have to perform n − 1 swaps to get items into their correct
positions. If each swap takes s time units, then the total time for all swaps
is s(n − 1). This operation is, therefore, O(n). Since the comparisons are
O(n2 ), the time for comparisons dominates the total time and so selection
sort is O(n2 ).
Both insertion sort and bubble sort are also O(n2 ) algorithms but
insertion sort usually requires more data movement than selection sort and
bubble sort usually requires far more data movement, making it slower
than either selection sort or insertion sort. Shellsort can provide dramatic
improvement in running time over any of the other three sorts that we have
studied. Although no exact analysis of the performance of Shellsort has yet
been possible, it has been found that with a good sequence of jump sizes,
the time taken by Shellsort is approximately O(n1.25). There are a number
of sorts that have even better performance than Shellsort. We will study
one such sort, quicksort, when we examine a technique called recursion in
Chapter 11.
Although our performance measures are not linked to specific com-
puters or programming languages, they can help us to determine actual
performance times. This is illustrated in the next example.
10.7. COMPARING ALGORITHMS 409
Example 1
A selection sort running on a particular computer requires 0.003 s to sort
200 items.2 Estimate the time that the sort would require on the same
machine to sort 8000 items.
Since selection sort is O(n2 ), the time taken by the algorithm is roughly
proportional to n2 so that
T =˙ k × n2
where k is a constant for a particular environment. For two separate runs
on the same computer, we would then have
T1 ˙ kn12
=
T2 ˙ kn22
=
T2 kn22
=
˙
T1 kn12
or
T2 n2
˙ 22
=
T1 n1
Substituting the values that we know, we get
T2 80002
=
˙
0.003 2002
T2 =
˙ 4.8
Exercises 10.7
1. If an algorithm is known to be O(n2 ) and it takes t s to process a
problem of size n, about how long would it take (in the same com-
puting environment) to process a similar problem of size
2 The
values used in this example and in the questions of the following exercises are
not intended to represent actual performance results of today’s computers.
410 CHAPTER 10. SEARCHING AND SORTING
Example 1
A selection sort for Sortable objects could have the form
public static void selectSort (Sortable[] list)
{
for (int top = list.length - 1; top > 0; top--)
{
int largeLoc = 0;
for (int i = 1; i <= top; i++)
if (list[largeLoc].precedes(list[i]))
largeLoc = i;
Sortable temp = list[top];
list[top] = list[largeLoc];
list[largeLoc] = temp;
}
}
412 CHAPTER 10. SEARCHING AND SORTING
• Specify the requirements that a class must satisfy if its objects are
to be considered sortable. There is only one such requirement: any
Sortable class must have a precedes method.
For the first part, we use a structure that we have not yet seen in Java —
an interface. Interfaces are very similar to the classes that we have been
using; they can contain both fields and methods. The definition of the
Sortable interface might take the following form.
interface Sortable
{
public boolean precedes (Sortable other)
;
}
Notice in the definition the word interface instead of class. Notice also
that the method of the interface has no body. This is characteristic of all
interfaces; their methods never have bodies. All that the definition does is,
in a sense, lay out the framework for what would be required of any class
that wants to be considered to be of type Sortable.
For the second part, for each class that we want to be designated as
Sortable, we must provide a precedes method with the signature given
in the interface. This method must define how to determine the ordering
of objects of that type. The next example illustrates how we can do this.
Example 2
Suppose that we want to be able to sort objects of the type Fraction
that we used when we introduced the idea of an object. Recall that the
Fraction class had two fields, num and den, representing the numerator
and denominator of a fraction. There is a natural way to decide if one
Fraction object precedes another: we simply examine the values of the
fractions that correspond to the Fraction objects. To implement this, we
rewrite the Fraction class as follows.
10.8. COMPARING OBJECTS 413
}
Notice the phrase implements Sortable in the first line. This tells Java
that there is a method in the Fraction class that provides a body for the
method precedes in the Sortable interface.
Notice also that the type of the parameter in the precedes method
is Sortable. The Sortable interface specifies that any precedes method
must accept a parameter that is of type Sortable. Within the method,
we cast the parameter to an appropriate type of object (Fraction in this
case) with the statement
Fraction f = (Fraction)s;
If we were writing a precedes method for a Widget class, we would have
to cast the Sortable parameter to an object of type Widget.
With this revised definition of the Fraction class, we can now use the
selectSort method of Example 1 to sort an array of Fraction objects
because Fraction objects can also be considered as Sortable objects. If
our program contained other classes that implemented Sortable, then ar-
rays of objects of these classes could also be sorted using exactly the same
method.
This idea is such a good one that a form of it is actually implemented
414 CHAPTER 10. SEARCHING AND SORTING
Example 3
An insertion sort that could be used for any objects that implement the
Comparable interface could take the following form.
Example 4
The following code shows what must be added to the Fraction class to
make it possible to sort Fraction objects using the sort in the preceding
example.
Example 5
The following program first creates three arrays: an array of Fraction
objects, an array of Integer objects used as wrappers for int values, and
an array of String values. After printing the contents of the original lists,
it then uses a single sort method to sort all three arrays of objects and
then prints the sorted results. The program assumes that the Fraction
class contains the phrase implements Comparable in its header and that
it contains definitions of the following methods: a compareTo method as
required by the Comparable interface, a constructor that accepts a numer-
ator and denominator as arguments, and a toString method that returns
a string of the form "a/b".
Notice the use (in the println statements) of the method intValue.
This method is used to recover int values from objects in the Integer
wrapper class.
String [] s = {"Jonathan","Callum","Melissa"};
System.out.println("Original Values\n");
for (int i = 0; i < f.length; i++)
System.out.println(f[i] + " " + n[i].intValue()
+ " " + s[i]);
selectSort(f);
selectSort(n);
selectSort(s);
System.out.println("\nSorted Values\n");
for (int i = 0; i < f.length; i++)
System.out.println(f[i] + " " + n[i].intValue()
+ " " + s[i]);
}
Original Values
3/4 7 Jonathan
1/2 5 Callum
2/3 2 Melissa
Sorted Values
1/2 2 Callum
2/3 5 Jonathan
3/4 7 Melissa
Exercises 10.8
1. Consider a class Student that has a field defined by
private int studentNumber;
Write a definition for a compareTo method for the Student class that
returns −1 if the studentNumber of its implicit parameter precedes
that of its explicit parameter, 0 if the studentNumber fields have
equal value, and +1 otherwise.
2. (a) Define a class Point with private double fields x and y that
represent the coordinates of a point. Your definition of the class
should include features that permit the use of the selectSort
method of Example 1 to sort an array of Point objects. For
Point objects p1 and p2, consider p1 to precede p2 if and only
if the first point is closer to the origin than the second.
(b) Change the definition of the Point class so that it implements
Java’s Comparable interface.
10.9. REVIEW EXERCISES 10 419
Bat Cat Cow Dog Elk Fly Fox Gnu Hen Owl Pig Rat Yak
trace the execution of a binary search as it looks for the value Man
in the list. In your trace, note the bounds of the sub-array and the
value in the list that is being examined on each pass. If a sub-array
has no exact middle, choose the item to the immediate left of the
middle.
2. Sequential search as given in the text does not require that the list of
items be sorted but, if the list is sorted, then sequential search can
be made more efficient. Write a method with header
public static int seqSearchSorted (String[] list,
String item)
that uses the fact that list is sorted in ascending order to make the
search more efficient. The method should, as usual, return the index
in list of the first occurrence of item or −1 if item is not in the list.
5 1 4 8 9 6 3
Show the list as it would appear after the first and second pass of
each sort.
a) selection sort b) insertion sort c) bubble sort
86 13 14 61 54 70 75 26 50 12 53 29 70 65
420 CHAPTER 10. SEARCHING AND SORTING
Show the list as it would appear after one pass of Shellsort with k = 5.
Projects
blank. For example, with the input shown above, the output would
be
Spades queen 6
Hearts ace jack 9 5 4 2
Diamonds 4
Clubs ace king 10 7
Value: 17 points
11. The Tenants’ Rights Bureau wants you to assist them in monitoring
complaints against apartment landlords. At the end of each month,
for each of the buildings in its files, the Bureau will give you the
building name, the number of units in the building, the number of
complaints received this month, and the name of the landlord. You
may assume that there are no more than one hundred buildings in
the Bureau’s files. The Bureau wants you to produce the following
lists:
(a) an unsorted list of the raw data, for checking purposes,
(b) a listing of the data, with suitable headings, sorted alphabeti-
cally by building name,
(c) a listing of the data (again with suitable headings) sorted alpha-
betically by landlord,
(d) another table, in alphabetical order by landlord, in which there
is only one entry for each landlord, each entry showing the total
number of units owned by that landlord and the total number
of complaints against that landlord,
(e) the name of the worst landlord of the month, decided by finding
the landlord having the highest percentage of complaints for
total units owned.
Chapter 11
Recursion
Example 1
To draw a family tree showing a person and all of the person’s direct
descendants, we can use the following algorithm.
To DRAW THE FAMILY TREE of a person
write the name of the person
if the person had children
for each child
draw a line from the person and
DRAW THE FAMILY TREE of the child
Example 2
Let us consider the structure of an onion without its brown papery covering.
Suppose that we want to describe the process of finding the centre of the
onion. If we peel away the outermost thick, fleshy layer, we are either at
the core or we are left with what looks like a smaller version of the original
onion. Thus, finding the centre of an onion could be described by the
following recursive procedure.
To FIND THE CENTRE OF THE ONION
if you are not at the core
remove one layer
FIND THE CENTRE OF THE ONION
The simplest case for finding the centre of the onion is finding that we
are at the core. In the more complex case, we consider an onion as being
composed of a fleshy layer that surrounds a smaller onion or the core. By
removing one layer, we get a little closer to the simple case.
Exercises 11.1
1. The following is a recursive definition of the process of climbing a set
of stairs.
3. An old children’s rhyme begins: “There once was a man who had a
hollow tooth. In the tooth was a treasure chest. In the treasure chest
was a piece of paper. On the piece of paper was written ‘There once
was a man who had a hollow tooth . . . ’ ”.
8. The diagram shows a family tree. If we were to draw this tree using
the recursive algorithm of Example 1, in what order would the mem-
bers of the family be inserted in the tree? (Assume that the children
of any individual are added from left to right. For example, Michael
would be added before David.)
11.2. RECURSION IN MATHEMATICS 427
Ted
Anne Gerry
3, 6, 12, 24, . . .
in which any term is twice as large as the preceding one. If we call the terms
t1 , t2 , t3 , . . . then we can express the relationship concisely as follows:
t1 = 3
tn = 2 × tn−1 , if n > 1
This definition of the terms of the sequence satisfies the conditions that we
set out for any recursive definition:
1. An object is defined in terms of another object of the same type.
Here a term is defined in terms of the preceding term.
Example 1
Given the sequence defined recursively by the equations
t1 = 2
tn = 3 × tn−1 + 1, if n > 1
t5 = 3 × t4 + 1
and t4 = 3 × t3 + 1
and t3 = 3 × t2 + 1
and t2 = 3 × t1 + 1
and t1 = 2
Having found the value of t1 , we can now substitute back into the expres-
sions for the other terms.
t2 = 3×2+1 = 7
and t3 = 3×7+1 = 22
and t4 = 3 × 22 + 1 = 67
and t5 = 3 × 67 + 1 = 202
One of the most famous algorithms known was stated over two thou-
sand years ago by the Greek mathematician Euclid. Euclid’s algorithm,
as it is called, provides a method of finding the greatest common divisor
(gcd) of a pair of natural numbers. The algorithm is based on the following
properties of a gcd:
Rule 1. The gcd of two equal values, m and n, is simply m (or n).
Rule 2. If two numbers are not equal, then the gcd of the two values
will also divide their difference and will, in fact, be the gcd of this
difference. Thus, if m > n, then the gcd of m and n will be equal to
the gcd of n and m − n.
Rule 3. If the numbers are not equal and the first number is smaller than
the second, then we can simply switch the numbers and apply Rule 2.
11.2. RECURSION IN MATHEMATICS 429
These three rules can be stated more concisely (and possibly more clearly)
in symbolic form:
Rule 1. If m = n, then gcd(m, n) = m
Rule 2. If m > n, then gcd(m, n) = gcd(n, m − n)
Rule 3. If m < n, then gcd(m, n) = gcd(n, m)
Example 2
Use Euclid’s algorithm to find the greatest common divisor of 54 and 90.
Exercises 11.2
1. Write the first five terms of each sequence (correct to two decimal
places).
(a) t1 = 3 (b) t1 = 2
tn = tn−1 + 2, n > 1 tn+1 = 1 − t1n , n > 0
(c) t1 = 2 (d) t1 = 1
tn+1 = n + tn , n > 0 t2 = 3
tn = tn−1 + tn−2 , n > 2
1 if n = 1 2 if n = 1
(e) tn = √ (f) tn = √
1 + tn−1 if n > 1 3tn−1 + 4 if n > 1
1 3 5
(a) 10, 13, 16, 19, . . . (b) , , ,...
2 2 2
4. A frog, sitting at one end of a two-metre log, jumps toward the other
end but can only make it half way. The frog continues jumping toward
the end but each time becomes more exhausted and only jumps half
the length of the preceding jump.
t1 = 2
tn = 3tn−1 − 1, n > 1
t(1) = 2
t(n) = 3t(n − 1) − 1, n > 1
Example 1
The following method returns the value of the function defined by
t(1) = 2
t(n) = 3t(n − 1) − 1, n > 1
return 3 * term(3) - 1;
The right side of this assignment statement involves a call to the method
term. We have seen methods call other methods before. In such a case,
execution of the first method is suspended until the called method returns
the value it has been asked to compute. The same thing happens here; the
only difference is that here both the calling method and the called method
are the same. To visualize the process, we can think of a new copy of term
being created by the call. This copy is identical to the original but it has
its own copy of the parameter n. This version of the parameter n has the
value 3. If the method had any local variables, there would also be separate
copies of them in both the original and the called versions of the method.
original call
term (n = 4)
executes
return 3 * term(3)- 1;
calls
term (n = 3)
term = 3 * term(2) - 1;
original call
term (n = 4)
executes
return 3 * term(3)- 1;
calls
term (n = 3)
executes
return 3 * term(2) - 1;
calls
term (n = 2)
executes
return 3 * term(1)- 1;
calls
term (n = 1)
executes
return 2;
The copy of term with n = 1 now returns the value 2 to the point from
which it was called and then ceases to exist. Knowing the value of term(1),
the value of the expression 3 * term(1) - 1 can now be completed; it
gives 3 × 2 − 1 = 5. This value can then be returned to the point at which
this version of term was called. This process continues until the original
call computes and returns the desired value. Once again, we can illustrate
the process with a diagram.
term (n = 1) returns 2
to term (n = 2) ?
which returns 3 × 2 − 1 = 5
to term (n = 3) ?
which returns 3 × 5 − 1 = 14
to term (n = 4) ?
which returns 3 × 14 − 1 = 41
Example 2
An efficient method for calculating the value of the function of the previous
example would use a loop. Here is an efficient, non-recursive method that
does the job.
Example 3
A more efficient form of Euclid’s algorithm than the one given in the pre-
vious section replaces the successive subtractions by one mod operation
(implemented by the % operator in Java). The revised algorithm for deter-
mining the gcd of two non-negative integers is
m if n = 0
gcd(m, n) =
gcd(n, m mod n) if n > 0
The algorithm can be implemented easily in Java, as shown in the following
method. (The method assumes that the values of both parameters are non-
negative integers.)
436 CHAPTER 11. RECURSION
calls ?
gcd (m = 18, n = 6)
executes
return
gcd(6,18 % 6);
calls ?
gcd (m = 6, n = 0)
executes
return 6;
Now, each copy of gcd returns, as shown in the next diagram.
gcd (m = 6, n = 0)
returns 6
to gcd (m = 18, n = 6)
which returns 6
to gcd (m = 24, n = 18)
which returns 6
to the point from which it was called
Exercises 11.3
1. Trace the execution of the method gcd in Example 2 to find the
greatest common divisor of each pair of values.
(a) m = 20 n = 28 (b) m = 991 n = 129
11.3. RECURSIVE QUERIES 437
2. Trace the execution of the method term shown below, given an initial
parameter value of 3.
Recursion is not limited to queries that return values. It can also be used
with methods that perform some action and then simply return to the
point at which they were called. To illustrate this, we will solve a problem
usually known as the Towers of Hanoi. In this problem, a number of disks
of decreasing size forms a “tower” on one of three needles. The smallest
disk is at the top of the tower and the largest is at the bottom, as shown
in the diagram.
1
2
3
q
q
n − 1
n
Needle 1 Needle 2 Needle 3
The problem is to move all of the disks from Needle 1 to Needle 3 (using
Needle 2 as necessary) subject to the following restrictions:
If there is only one disk on Needle 1, then the problem is trivial as we can
simply move the disk from Needle 1 to Needle 3. We will write this move
in the form 1 −→ 3.
A tower with two disks can be thought of as a one-disk tower placed
on top of a larger disk. The instructions to move such a tower could be
phrased in the following form:
1
2
3
q
q
n n − 1
Needle 1 Needle 2 Needle 3
1
2
3
q
q
n−1 n
Needle 1 Needle 2 Needle 3
1
2
3
q
q
1
n −
n
Needle 1 Needle 2 Needle 3
The second part of the solution is trivial; it simply requires the moving
of one disk. The first and third parts, while they do not have obvious
solutions if n is large, are problems that have the same form as the original
problem except that they require that we move a tower that is smaller (by
one disk) than the original. This is exactly what we want for a recursive
solution — the solution to the original problem (moving a tower of n disks)
has been stated in terms of twice solving a simpler problem of the same
type (moving a tower of n − 1 disks).
The preceding analysis suggests (we hope) the following algorithm:
if (height == 1)
// there is only one disk - simply move it
System.out.println(start + " ---> " + finish);
else // solve the problem recursively
{
// determine the other needle number using
// the fact that the sum of the values of the
// three needle numbers is always six.
int other = 6 - (start + finish);
Example 1
If we were to call this method with the statement moveTower(2,1,3); then
the call would indicate that we want to move a tower of height two from
Needle 1 to Needle 3. The call would produce the following output.
1 ---> 2
1 ---> 3
2 ---> 3
moveTower
height: 3
start: 1
finish: 3
1m 1 ---> 3 @ m
other: 2
I
@@ 12
6m 7m
@@
@ @
moveTower R moveTower
@
height: 2 height: 2
start: 1 start: 2
finish: 2 finish: 3
other: 3 other: 1
2m C CO 5m 8m m
1 ---> 2 2 ---> 3
3m 4m 9m 10m
C OC 11
CC CC
CW C WC C
moveTower moveTower moveTower moveTower
height: 1 height: 1 height: 1 height: 1
start: 1 start: 3 start: 2 start: 1
finish: 3 finish: 2 finish: 1 finish: 3
1 ---> 3 3 ---> 2 2 ---> 1 1 ---> 3
Exercises 11.4
1. (a) In the Towers of Hanoi problem, the number of moves grows
rapidly as the number of disks increases. By following the algo-
rithm given in the text, complete the following table.
Number of Disks Number of Moves
1 1
2 3
3
4
5
(b) Using the pattern of values in the table, predict the number of
moves that would be required for
i) 6 disks ii) 7 disks iii) 10 disks iv) n disks
444 CHAPTER 11. RECURSION
*
**
***
****
*****
8. What will the method mystery do if called with the argument 12345?
Example 1
To reverse the characters in a string, we could use the following algorithm.
To REVERSE a string
if the string is non-empty
place the first character at the end of
the REVERSE of the tail of the string
For the simple case, an empty string, the reversal is simply the empty string
itself. Here is a Java method that implements the algorithm. Given any
string, the method will return the reversal of that string.
public static String reverse (String s)
{
if (s.length() > 0)
return reverse(s.substring(1)) + s.charAt(0);
else
return "";
}
else
for each character in the string from here to the end
PRINT STRINGS WITH ALL PERMUTATIONS of remaining
characters starting with that character
The next example shows a Java implementation of the algorithm.
Example 2
The method that follows will print all permutations of the string s that
can be obtained by rearranging the characters from position index to the
end of the string. We can use it to find all permutations of a string by
calling it with index equal to zero.
public static void permute (String s, int index)
{
if (index == s.length())
System.out.println(s);
else
for (int i = index; i < s.length(); i++)
permute(s.substring(0,index)+s.charAt(i)
+s.substring(index,i)+s.substring(i+1),index+1);
}
Example 3
The pair of methods that follow, both called permute, work together to
find and print all the permutations of a string.
Exercises 11.5
1. In Example 1, we developed and implemented an algorithm for re-
versing a string. Another approach to this problem is to proceed as
follows:
To REVERSE a string
if the string is non-empty
place the last character at the front of
the REVERSE of the rest of the string
Our problem is, given a starting point in the maze, to find an exit
point, if there is one accessible from the given starting point. If the search
for an exit is successful we also want to be able to see the path that has
been found.
If we look at the problem from the rat’s point of view, we can establish
a recursive solution. The simplest case is finding ourselves at an exit. In
this case, there is nothing more to do. If we are not at an exit, then we
can try to find one by moving to an adjacent position that we have not
yet tried and seeking an exit from there. (The rat can probably tell by
smell which of the adjacent open positions have not been tried; we may
have to use some other device.) If there are no open positions adjacent to
our current one, then we must back up along the path that we took to get
to this spot. This is where the idea of backtracking arises. If we ever get
backed up to our original position without having found an exit and with
no further paths to try, then we must give up — no exit can be reached
from our starting point.
To enable us to keep track of the locations that we have tried, we use
the character ’-’ which we call TRIED. Whenever we visit a location for
the first time, we place a TRIED character in that spot. If we ever find a
path that leads to an exit, we mark the locations along that path with the
character ’+’ which we call GOOD_PATH. If an exit is found from an initial
location, we place a sequence of GOOD_PATH characters in the maze, from
the starting location to the exit.
Example 1
We can begin to define a Maze class as follows:
class Maze
{
private static final char WALL = ’W’;
private static final char EXIT = ’X’;
private static final char OPEN = ’.’;
private static final char TRIED = ’-’;
private static final char GOOD_PATH = ’+’;
private char[][] map;
}
452 CHAPTER 11. RECURSION
Example 2
The method findExitFrom that follows attempts to move from the starting
point in the array maze whose indices are given by the parameters row and
col. The method assumes that the perimeter of the map array contains
only WALL or EXIT symbols. It also assumes that the position specified by
row and col initially contains either an OPEN or EXIT symbol.
The method first checks to see if the current location is an exit and, if
it is, the method returns the value true. Otherwise, it marks the current
position with the symbol Maze.TRIED. This ensures that the method will
not wander around the maze forever if it is forced to back up. The method
then tries to move to an adjacent position in the maze. If the search for
an exit from an adjacent point proves to be successful, the method marks
the current position with the symbol Maze.GOOD PATH indicating that this
position is on a path that leads to an exit.
if (map[row][col+1] == Maze.OPEN
|| map[row][col+1] == Maze.EXIT)
successful = findExitFrom(row,col+1);
if (!successful)
// could not find exit in east - try moving north
if (map[row-1][col] == Maze.OPEN
|| map[row-1][col] == Maze.EXIT)
successful = findExitFrom(row-1,col);
if (!successful)
// could not find exit in north - try moving west
if (map[row][col-1] == Maze.OPEN
|| map[row][col-1] == Maze.EXIT)
successful = findExitFrom(row,col-1);
if (successful)
// mark this as part of a correct path
map[row][col] = Maze.GOOD_PATH;
}
return successful;
}
Exercises 11.6
1. Trace the path that would be taken by the method findExitFrom if it
were given the maze shown in the text, starting at map[1][8]. Give
your answer as a sequence of coordinates representing the row and
column of each location visited — from the starting position (1, 8) to
the exit position (6, 7).
2. Show the contents of the array map after the maze has been solved
by the method findExitFrom.
4. Write an instance method print for the Maze class. The method
should print the map array in rectangular form.
454 CHAPTER 11. RECURSION
5. Write a constructor for the Maze class that takes two parameters: the
width and height of the array. The constructor should prompt the
user for the contents of the array, one row at a time, as a sequence
of strings. If a user provides a string that is invalid, the construc-
tor should reject it and prompt, repeatedly if necessary, for another
string for that row. For a maze to be valid, it must contain only
the characters for a wall, an exit, or an open cell. In addition, the
perimeter must contain only wall or exit characters.
** *
**** *** **
** ** ****
*** **** ****
* ** ****
****** **
** *
** ******
* **
We say that two asterisks are connected if they are adjacent vertically,
horizontally, or diagonally. We call a group of connected asterisks a
blob. The diagram contains three blobs.
Consider the problem of starting at any point in a blob and erasing
the entire blob, leaving any other blobs intact.
11.7 Quicksort
When we last looked at sorts, the fastest one that we saw was Shellsort.
Now, using recursion, we can examine an even faster algorithm, named
quicksort by its inventor, C. A. R. Hoare.2
Quicksort, like all of our previous sorts, uses a number of passes to
achieve its goal of ordering an entire list of values. Like insertion sort,
quicksort inserts one more item into the list on each pass. However, unlike
insertion sort, quicksort inserts items into their final positions. Once an
item has been inserted by quicksort, it never moves again.
The sort is based on the following very simple idea. If a list is sorted
(in ascending order, say) then, if we look at any value, K, all the values
less than K will be on its left and all values greater than K will be on its
right, as shown in the next diagram. (We assume for now that all values
are unique; it is simple to adapt the sort to cases where there are repeated
values.)
Values less than K K Values greater than K
Example 1
Suppose that we are given the following list of integers:
61 46 12 63 52 91 27 55 74 14 71 37 87
2 Quicksort and, in fact, any recursively defined process, can be done without recursion
but it is easier to describe this way.
456 CHAPTER 11. RECURSION
We begin our first pass of quicksort by choosing one element to be the pivot.
We arbitrarily choose the left hand element, 61. The pass now proceeds
to divide the list so that values less than 61 are on the left end of the list,
values greater than 61 are on the right end of the list, and 61 is between
these two sets of values, in its final, correct, position. To do this, a copy is
made of the pivot (61 in our case) and two markers, left and right, are
set to point to the leftmost and rightmost locations in the list, as shown in
the next diagram.
61 61 46 12 63 52 91 27 55 74 14 71 37 87
↑ ↑ ↑
pivot left right
The right marker starts moving toward the centre looking for values that
should not be on the right. Since 87 > 61, 87 is allowed to remain where it
is. Since 37 < 61, it should go to the left. To get it there, it is copied into
the location indicated by the left marker, formerly occupied by 61. This
gives
61 37 46 12 63 52 91 27 55 74 14 71 37 87
↑ ↑ ↑
pivot left right
Now the left marker starts moving toward the centre looking for values
that should not be on the left because they are greater than the pivot.
Since 46 < 61, 46 is allowed to remain where it is. Since 12 < 61, it is also
allowed to stay in its location. Since 63 > 61, it is copied into the location
marked by right. This gives
61 37 46 12 63 52 91 27 55 74 14 71 63 87
↑ ↑ ↑
pivot left right
Again, right moves toward the centre, looking for values that are on the
wrong end of the list. It finds 14 and sends it to the location marked by
left, giving
61 37 46 12 14 52 91 27 55 74 14 71 63 87
↑ ↑ ↑
pivot left right
Now left moves toward the centre, finding 91 on the wrong side and
sending it to the right.
11.7. QUICKSORT 457
61 37 46 12 14 52 91 27 55 74 91 71 63 87
↑ ↑ ↑
pivot left right
Again, right moves toward the centre, finding 55 on the wrong side and
sending it to the left.
61 37 46 12 14 52 55 27 55 74 91 71 63 87
↑ ↑ ↑
pivot left right
Finally, left moves toward the centre and this time finds right before it
detects any more values on the wrong side. The cell that both left and
right meet at is the correct location for the pivot. It is copied into that
spot to give the following arrangement at the end of the pass.
61 37 46 12 14 52 55 27 61 74 91 71 63 87
↑ ↑
pivot left/right
If all goes well on the first pass of quicksort, one value (the pivot)
is inserted near the centre of the list with all lesser values on its left and
all greater values on its right. As we said earlier, if the list values were
completely sorted, the pivot would have exactly the same values on its left
and on its right as it does after the partitioning. Thus the pivot must be in
its final position after the first pass; we no longer have to worry about it.
What we do have to worry about after the first pass are the two unsorted
sublists on either side of the pivot. To take care of them, we apply our
partitioning technique recursively on each sublist.
Example 2
Using the list produced in Example 1, let us see how the list would appear
if the partitioning technique used there is applied to the sublists on the left
and right side of the original pivot.
For the left sublist, we take the pivot to be its leftmost item, 37. The
diagrams show the progress of the left and right markers as they seek
out the correct position for 37 in the sublist.
458 CHAPTER 11. RECURSION
37 37 46 12 14 52 55 27
↑ ↑ ↑
pivot left right
37 27 46 12 14 52 55 27
↑ ↑ ↑
pivot left right
37 27 46 12 14 52 55 46
↑ ↑ ↑
pivot left right
37 27 14 12 14 52 55 46
↑ ↑ ↑
pivot left right
37 27 14 12 37 52 55 46
↑ ↑
pivot left/right
For the right sublist, we have the following stages in the insertion of the
pivot 74 in its correct position.
74 74 91 71 63 87
↑ ↑ ↑
pivot left right
74 63 91 71 63 87
↑ ↑ ↑
pivot left right
74 63 91 71 91 87
↑ ↑ ↑
pivot left right
74 63 71 71 91 87
↑ ↑ ↑
pivot left right
74 63 71 74 91 87
↑ ↑
pivot left/right
11.7. QUICKSORT 459
Example 3
The methods that follow, both called quickSort, work together to imple-
ment quicksort to sort an array of Object values. They work whether or
not there are repeated items in the array. If we were to place the meth-
ods in a class called Utilities then, to perform a sort on an array called
myList, a user would write
Utilities.quickSort(myList);
This would invoke the first method and it would then call the second, with
the appropriate bounds for the first pass of the sort. The second version,
a helper method, would then call itself recursively on the sub-intervals.
public static void quickSort (Object[] list)
{
quickSort(list,0,list.length-1);
}
{
if (currentDirection == MOVING_LEFT)
{
while (list[right].compareTo(pivot) >= 0
&& left < right)
right--;
list[left] = list[right];
currentDirection = MOVING_RIGHT;
}
if (currentDirection == MOVING_RIGHT)
{
while (list[left].compareTo(pivot) <= 0
&& left < right)
left++;
list[right] = list[left];
currentDirection = MOVING_LEFT;
}
}
list[left] = pivot; // or list[right], since equal
quickSort(list,low,left-1);
quickSort(list,right+1,high);
}
}
Exercises 11.7
1. Given the following list, show the stages in quicksort’s partitioning of
the list (as in Example 2) if the element 12 is used as the pivot and
the list is to be sorted in ascending order.
12 7 20 6 21 18 2 14 15 5
3. Repeat the previous question but now suppose that the list is already
sorted before quicksort is called so that, at every partition, the pivot
element stays at the left end of the list.
5. Write a program that first generates 500 random integers in the in-
terval 0 . . . 999, prints the values, uses a version of quicksort to sort
the values in descending order, and prints the sorted values.
mn = mn−1 + 1 + mn−1
= 2mn−1 + 1
mn−1 = 2mn−2 + 1
mn = 2(2mn−2 + 1) + 1
= 22 mn−2 + 2 + 1
mn = 22 (2mn−3 + 1) + 2 + 1
= 23 mn−3 + 22 + 2 + 1
mn = 2k mn−k + 2k−1 + · · · + 22 + 2 + 1
mn = 2n−1 + 2n−2 + · · · + 22 + 2 + 1
11.8. ALGORITHM ANALYSIS REVISITED 463
mn = 2n − 1
Since the time required to perform the algorithm will be roughly propor-
tional to the number of moves, this gives us
T is O(2n )
How many times must we repeat this process before we are done? At
each stage, the size of each sublist is being cut (approximately) in half
and the process continues until the size is reduced to one item. This is a
situation that we have seen and analyzed previously, when we studied the
behaviour of binary search. There we showed that lg n steps are required
to reduce the list size to one. Here, however, at each of these lg n steps, we
must perform approximately n comparisons. Thus quicksort will require a
total of n × lg n comparisons to completely sort a list. Hence, under our
assumption that the pivots will be in the middle of their lists, quicksort is
an O(n lg n) algorithm.
Although it is almost always true that, as a problem size gets larger,
we have to do more work to solve the problem, this is not always the case.
For example, the amount of work required to retrieve a value from an array
does not depend on the size of the array. If the time required to perform
an operation is no more than some constant, k, then we can write
T is O(k)
To give some feeling for the range of times required by various algo-
rithms, the next table gives approximate values of a number of the functions
that we have encountered in our analysis of algorithms.
n lg n n lg n n2 2n
10 3 30 100 1 000
100 7 700 10 000 1030
1 000 10 10 000 1 000 000 10300
10 000 13 130 000 100 000 000 103000
Exercises 11.8
1. An algorithm whose running time is known to be O(2n ) requires t s
to process a problem of size n. About how long would it take (in the
same computing environment) to process a similar problem of each
of the given sizes?
(a) n + 1 (b) n + 3
(c) 2n (d) 3n
Avoiding Errors
1. The most common error in using recursion is writing something that
never terminates. This can be avoided by always checking that
(a) there is a simple case in which the method does not make a
recursive call and
(b) any recursive call is a step closer to the simple case.
2. One way to avoid problems with recursion is to simply avoid using
recursion at all. Although recursive solutions to problems are often
more elegant than non-recursive ones, it is always possible to replace
a recursive solution with a non-recursive one. For many problems,
a non-recursive approach is more appropriate. There is a certain
amount of overhead in using a recursive solution because every time
a method is called recursively, time is used by the calling process and
more space must be allocated for any parameters and local variables.
If a simple iterative solution exists, it should be used in preference to
a recursive one.
Debugging
Exercises 11.9
1. State the error in each method.
(a) public static int f (int n)
{
if (n >= 0)
return f(n-1) * n;
}
(b) public static double g (double x)
{
if (x > 5)
return g(2*x) + 1;
else
return 7;
}
2. Write a method (with a single int parameter n) that provides a non-
recursive solution to the problem of computing the value of the nth
term of the sequence whose recursive definition follows. If n < 1, the
method should return zero.
t1 = 2
tn = 3tn−1 − 1, if n > 1
t1 = 1
t2 = 2
t3 = 3
tn = tn−1 + tn−3 , if n > 3
468 CHAPTER 11. RECURSION
2. Suppose that you have two unsorted piles of cards (A and B) with
each card in each pile containing the title of a book. Your task is to
find all titles that appear in both piles and write them on a piece of
paper without sorting the cards in either pile. Give a recursive de-
scription of this process. You may assume that there are no duplicate
titles in either pile.
3. (a) State, in a few words, the effect of the function f whose definition
(for non-negative integers x and y) is
0 if y = 0
f(x, y) =
x + f(x, y − 1) otherwise
1, 1, 2, 3, 5, 8, . . .
(b) How many calls are made to fibonacci in computing the value
of the fifth term of the Fibonacci sequence?
(c) Write a non-recursive version of fibonacci.
(d) Which version of fibonacci is more efficient? Why?
****
***
**
*
**
***
****
46 = ((4)2 × 4)2
= (16 × 4)2
= (64)2
= 4096
(a) Use the given relationships to write each power in factored form.
i) x10 ii) x25 iii) x100
(b) How many multiplications would be needed to evaluate each of
the factored expressions found in part (a)?
(c) Write a recursive method power, with double parameter x and
int parameter n, that uses the given relationships to find the
value of xn . In your method, assume that n > 0.
(d) Extend your method so that it gives correct answers for any
integral exponent. (The method should return NaN as the value
of 00 .)
p(x) = a0 + a1 x + a2 x2 + · · · + ai xi + · · · + an xn
Projects
13. Although some equations can be solved directly using a formula, for
many equations it is impossible to do so. For such equations we
can, however, usually obtain approximations to the roots that are
472 CHAPTER 11. RECURSION
ax3 + bx2 + cx + d = 0
Q
Q
Q
Q
1 3 0 2
Using such an array, we can attempt to place the queens on the board
from left to right. For each column, we attempt to find a safe row for a
queen (one not under attack from previously placed queens), placing
a queen there, and then making a recursive call to find a complete
solution given that placement. To check that a placement is safe from
attack by previously placed queens, it is easy to determine whether
474 CHAPTER 11. RECURSION
The MERGE phase takes the two sorted halves of a list and combines
them into one sorted list. For example, if, before a merge, a list
contained
12
| 18 {z 40 72} 20
| 32 {z 45 63}
sorted sorted
then, after the merge, the list would contain
12
| 18 20 32 {z 40 45 63 72}
sorted
475
A: In today’s economy, many organizations have great vision for technology
solutions, but lack the capital investment. Justifying the cost of IT expendi-
tures has always been a real challenge for many organizations. Then, once
solutions are implemented, organizations have an even harder challenge
measuring success. Every project should have clearly defined, measurable
success factors, so an organization knows when it can say “job well done”.
Also, the disciplines of project management and change management
provide challenges to many organizations I have consulted with. I have
served twenty-two clients over twenty years, and really only a small handful
had the project and change management structures in place required to
successfully implement technology solutions.
Q: What do you see as the “next big thing” in IT?
A: Predicting “next big things” is always a risky business, but I firmly believe
that more emphasis will be placed on increasing business functionality in a
wireless world — to be able to not just communicate with anyone, anytime,
anywhere, but to be able to execute business transactions anytime and
anywhere.
Q: What career advice do you have for someone completing a computing sci-
ence degree today?
A: The strongest advice I would give to anyone considering a career, in any
field, is to find something that you have a passion for and make it your
career. I really don’t have a job or career; I have a hobby that pays me well.
If you find what you love to do and have a passion for it, you will naturally
succeed. I firmly believe that I have been successfully self-employed for ten
years because I believe in what I do, I have a passion for my work, and I
strive for business excellence.
IT has so many facets (communication, programming, database de-
sign, project management, systems analysis), that you should try as many
IT functions as you can before deciding which stream to specialize in. Ex-
perience as much as you can before focusing on one area.
Lastly, of course, you need to obtain as many academic credentials as
possible. Education is one of the most critical foundations for a successful
career. My degree from the University of Waterloo opened doors for me
that would have not been opened otherwise. I very much enjoyed my time
at Waterloo and firmly believe my studies helped pave the way for my
success.
476
Chapter 12
These ideas about linear lists do not make any reference to the way
that a list might be stored in a computer; they are concerned only with the
abstract concept of a list. Because of this, we say that such a definition
12.1. LINEAR LISTS AND LINKED LISTS 479
Example 1
If we wanted a list to be used by air traffic controllers keeping track of
planes waiting for takeoff, we might define the required linear list ADT
as having nodes that contain information about planes. The operations
that would be required might be: create a new, empty list (at the start of
the day), insert a new node at the rear of the list (as a plane leaves the
terminal), delete a node from the front of the list (as a plane takes off),
and check to see if there are any nodes in the list (if a runway becomes
available).
List
head
Node Node Node
As the diagram implies, we use (at least) two kinds of objects to create
linked lists in Java: a single object of type List and a sequence of objects
480 CHAPTER 12. DYNAMIC DATA STRUCTURES
of type Node. The List object contains a reference to the first Node and
each Node except the last contains a reference to the next Node in the list.
In addition, the info fields shown in the diagram may also be references
to objects containing the information carried by the list.
As usual, the electrical ground symbol on the last node indicates a
null reference. An empty list has the form
List
head
class List
{
private Node head;
class Node
{
int info;
Node link;
List
-
head
If we now wanted to insert a single node containing the value item into
this empty list, we could write the following method for the List class.
public void insertFirst (int item)
{
head = new Node(item,null);
}
Suppose that this method were called by the statement
myList.insertFirst(6);
To execute the method, Java would first evaluate the right side of the
482 CHAPTER 12. DYNAMIC DATA STRUCTURES
assignment. This would cause a new node to be constructed with the info
field having the value 6 and the link field having the value null. The
assignment would then alter head so that it would refer to the new node
as shown in the next diagram.
myList
List
-
head
Node
- info 6
link
If we had created the structure shown in the previous diagram and then
executed the statement
myList.insertSecond(3);
the result would be the following structure in which a new node has been
created and head.link, the link field from the first node, has been set to
refer to this new node.
myList
List
-
head
Node Node
- info 6 - info 3
link link
12.1. LINEAR LISTS AND LINKED LISTS 483
To be able to add a third node to the list, we could write another method
that would create a new node and attach it to the link field of the second
node. The second node is referred to by head.link, the link field of
the first node. Thus, the link field of the second node has the identifier
head.link.link. Using this, we could then create the method insert-
Third:
Example 2
This method adds a node to the front of a linked list.
To execute the method, Java evaluates the right side of the assignment,
causing a new node to be constructed with the link field referring to the
same place that head refers — the front of the list. The assignment then
alters head so that it now refers to the new node. If, before the method
was called, a list had the form
484 CHAPTER 12. DYNAMIC DATA STRUCTURES
myList
List
-
head
Node Node
- info 4 - info 8
link link
Example 3
The method printList (in the List class) traverses a linked list of the
form shown in this section, printing the contents of the info field of each
node in the list.
12.1. LINEAR LISTS AND LINKED LISTS 485
List
head ?
Node Node Node
After a value has been printed by the println statement, the statement
temp = temp.link;
in the header of the loop causes the value in temp.link to be copied into
temp. Since temp.link is a reference to the next node in the list, temp will
now refer to that node also, as shown in the next diagram.
temp
List
head ?
Node Node Node
Each time we go through the loop, temp moves along to refer to the next
node, eventually traversing the entire list. When the value of the last
486 CHAPTER 12. DYNAMIC DATA STRUCTURES
node’s link field is assigned to temp, it will become null and the loop will
terminate, as required.
Once we have finished with a list, it is easy to get rid of the nodes. All
that we have to do is write
head = null;
This will cause the reference to the first node to be lost and, since the
only way that we can get to the other nodes is via the links starting from
the first node, execution of the statement causes us to lose contact with
the entire list. In some languages these now useless cells would clutter
up memory for the rest of the life of the program unless the programmer
explicitly disposed of them but, in Java, a program known as a “garbage
collector” automatically frees up the space occupied by any inaccessible
objects. Luckily, we do not have to worry about the details (in this course).
Exercises 12.1
1. In the diagram, ref is a reference to an object of the type Node
discussed in this section. The field marked A can be referred to as
ref.info. Find similar names for the fields marked B, C, and D.
ref
?
Node Node Node
info A - info - info D -
link B link C link
4. Write an instance method deleteFirst for the List class that deletes
the first node of a linked list. If the list is empty, the method should
print a warning message.
5. Write an instance method deleteLast for the List class that deletes
the last node of a linked list. If the list is empty, the method should
print a warning message.
6. Write a toString method for the List class. The method should
return a string that contains all the info fields of the list, separated
by //. For example, if the list contained the integers 3, 5, and 8 in
its info fields, the method should return "3//5//8".
7. Write an instance method addAtRear for the List class. The method
should have a single int parameter called item. The method should
first locate the last node in a list and then create and attach a new
node, containing the value of item, at that end of the list. In writing
your method, be sure that it works correctly for an empty list.
Node
newNode
- info 14
link
Node
newNode
- info 14
link
6
Node Node Node Node
info 10 info 13 info 20 info 25
- - -
link link link link
The insertion is carried out in two phases: first the correct location
for the new node is found and then links are adjusted so that the node
is inserted in the list at this point. To find the correct location for the
value 14, we traverse the list using an auxiliary reference variable until we
reach the node containing the next largest value, 20. The new node should
be inserted just before this one. At this point, it is easy to link the new
node to the node containing 20 but we cannot go back along the list to
link the previous node, containing 13, to the new node because the links
only point forward, not backward. One solution to this problem is to use
two auxiliary reference variables in our traversal: one to the current node
being examined and the other to the previous node. With these variables
we can, if necessary, link the previous node to the new one. This process
is implemented in the next example.
12.2. MORE OPERATIONS ON LINKED LISTS 489
Example 1
The method insert will insert a node containing the value item in its info
field in a linked list in which the info fields of the nodes are in ascending
order.
Example 2
This method searches an unordered linked list for a node containing the
value item in its info field. If it finds such a node, it deletes it from the
list. If there is no such node, the method does nothing; if there is more
than one node containing item, only the first is deleted.
Exercises 12.2
1. The diagram shows a portion of a linked list. In the diagram, both
current and temp are references to objects of the type Node that we
have been studying while item is of type int.
12.2. MORE OPERATIONS ON LINKED LISTS 491
@
R
@
Node Node Node
info 24 info 30 info 35
- - - -
link link link
Draw a diagram, similar to the one given here, illustrating the result
of executing the following fragment.
2. Write an instance method called contains for the List class. The
method should have header
public boolean contains (int item)
The method should return true if and only if its implicit List object
contains item.
3. In Chapter 10, we developed a fast searching method (binary search)
that took advantage of the fact that a sorted array can be searched
much more efficiently than an unsorted one. If the nodes of a linked
list are ordered, can we write a form of binary search for a linked list?
Justify your answer.
4. Write an instance method deleteAll for the List class. The method
should have one int parameter, item. It should delete all nodes in
the list that contain item in their info fields.
5. Complete the definition of the method whose header is
public boolean isOrderedIncreasing ()
The method should return true if and only if the info fields of the
implicit List object are in strictly increasing order. Assume that the
info fields are references to objects for which a compareTo method
exists.
6. The technique shown in Question 1 can be used to insert a value
into an ordered list without using two auxiliary references. Write an
492 CHAPTER 12. DYNAMIC DATA STRUCTURES
instance method that uses this technique to achieve the same effect
as the method shown in Example 1. (Be sure that your method works
at both ends of the list.)
Stacks
The first of these is the stack — a linear list in which all insertions,
deletions, and inspections take place at one end. The end at which activ-
ity takes place is called the top of the stack; the other end is called the
bottom of the stack. Stacks have a physical analogy in the spring-operated
mechanisms sometimes used to store stacks of plates in cafeterias. In these
devices, if you place a new plate on the top of the stack, the added pres-
sure pushes the stack down a bit; if you remove a plate from the top, the
decreased pressure allows the remaining plates to pop up a bit.
12.3. STACKS AND QUEUES 493
q q
q q
q q
q q
q q
q q
q q
q q
q q
q q
• push add a new item onto one end of the list (the top)
Stack Node
top - info
link
?
Node
info
link
pp
?
p
Node
info
link
Example 1
To insert an item in such a stack, we can use the following method (in the
class Stack):
public void push (int item)
{
top = new Node(item,top);
}
Example 2
This method (for the class Stack) returns the value currently at the top of
the stack without altering the stack.
public int peek ()
{
if (top == null)
throw new RuntimeException("peek: empty stack");
else
return top.info;
}
12.3. STACKS AND QUEUES 495
Notice that if the method finds that the stack is empty, there is no value
for the method to return. In such a situation, the method throws an excep-
tion. Unless we take some action to handle the exception, the program’s
execution will terminate. Exceptions and exception handling are discussed
in Appendix E.
The other methods required for a stack (pop and isEmpty) are also
quite simple. Their implementations are left as exercises.
Queues
A queue is another linear list with restrictions on insertions and dele-
tions. A queue operates like a human queue at a checkout line in a store.
All insertions take place at one end, the rear of the queue, and all deletions
take place at the other end, the front of the queue. Because the first item
inserted into a queue will be the first item deleted, a queue is also known
as a First In, First Out, or FIFO list. As we did for a stack, we can make
the idea of a queue as an ADT more precise. A queue is a linear list with
the following operations:
• enqueue add a new item onto one end (the rear) of the list
• dequeue delete and return the item at the other end (the front) of
the list
Queue
front rear
? ?
Node Node Node
- ppp
info info info
-
link link link
We can begin to create a Queue class by setting up fields for the references
to the two ends of the lists along with our usual Node inner class.
class Queue
{
private Node front;
private Node rear;
class Node
{
int info;
Node link;
Example 3
This method inserts a new node into a list of the Queue class.
public void enqueue (int item)
{
Node temp = new Node(item,null);
if (rear == null)
// queue was empty
front = rear = temp;
else
// add node at rear of queue
rear = rear.link = temp;
}
Exercises 12.3
1. (a) The Back operation on a Web browser is an application that
uses a stack. Give two other common applications of stacks.
(b) Find two common applications of queues.
2. Write instance methods to implement the pop and isEmpty opera-
tions of the Stack class. Have your pop method throw an exception
(as in Example 2) if the stack is empty.
3. Write instance methods for the Queue class that implement the peek
and dequeue operations. Each of the methods should throw an ex-
ception (as in Example 2) if the queue is empty.
4. In our implementation of a queue, the links in the nodes pointed
backward, from the front of the queue to the rear. If the links were
in the other direction, would it still be possible to insert and/or delete
items efficiently? Justify your answer in each case by writing a suit-
able method if you think that it is possible or by explaining why you
think that it is not possible.
5. A model of a stack is provided by a railroad switching network as
shown in the next diagram.
498 CHAPTER 12. DYNAMIC DATA STRUCTURES
Output Input
Stack
(a) Find all the possible output sequences that can be produced
from the input sequence 1, 2, 3.
You may recall that every recursive process has two parts: a case in which
the process is defined in terms of a simpler version of itself and a simple
case that terminates the recursion. It is often useful to think of structures
in a similar way. To do so with linked lists, we can think of a linked list
as a structure that is either empty (the simple case) or consists of a node
followed by a linked list — the tail of the original linked list.
12.4. RECURSIVE LIST PROCESSING 499
Example 1
If a linked list has nodes consisting of info and link fields, then, to print
the contents of the info fields, we can use an algorithm of the following
form.
To PRINT THE LIST
if the list is not empty
print the info field of the first node
PRINT THE LIST that forms the tail
Using this outline, we can write the method in Java. As a first attempt,
we could try the following method in the List class:
public void printList () // a first attempt
{
if (head != null)
{
System.out.println(head.info);
head.link.printList();
}
}
This appears to follow the algorithm but it doesn’t work! The problem is
that, since printList is an instance method of the class List, it must be
500 CHAPTER 12. DYNAMIC DATA STRUCTURES
Example 2
The printList methods shown here illustrate correct recursive printing of
a linked list.
class List
{
private Node head;
void printList ()
{
System.out.println(info);
if (link != null)
link.printList();
}
}
}
Example 3
This is a recursive algorithm for insertion of a node containing item in the
info field in an ordered list.
Once again, we will not use the algorithm in this simple form. Instead, we
will create two versions of the insertion method — one for the List class
and one for the inner Node class. The details are given in the next example.
502 CHAPTER 12. DYNAMIC DATA STRUCTURES
Example 4
This non-recursive method, for the List class, will start the process of
insertion of a node in an ordered list. The method calls the recursive
version only if the new item should be inserted somewhere after the first
node; otherwise, it does the insertion itself.
public void insert (int item)
{
if (head == null || item < head.info)
// insert item as first node of original list
head = new Node(item,head);
else
// call recursive version to
// insert item in tail of non-empty list
head.insert(item);
}
This recursive helper method, for the Node class, will insert item in its
correct position in the tail of a non-empty ordered list. It should never be
called if the new item is to be the first node in the list.
void insert (int item)
{
if (link == null || item < link.info)
// insert item right after first node
link = new Node(item,link);
else
// insert after first node of non-empty tail
link.insert(item);
}
Exercises 12.4
1. In Example 2, in the printList method (in the Node class), what
would be the effect of placing the println statement after the state-
ment that calls printList?
12.5. BINARY TREES 503
Complete the solution by writing the recursive copy method for the
Node class.
Although linear lists are useful for many applications, more complex struc-
tures are often required. In this section, we will begin to examine another
extremely useful data type — the binary tree.
504 CHAPTER 12. DYNAMIC DATA STRUCTURES
Example 1
The following diagram shows a binary tree containing the nodes A, B, . . . , I.
A
Z
Z
Z
B C
J J
J J
D E F G
J
J
H I
class Tree
{
private Node root;
}
Tree
root
A node of the tree will need three fields: a link to the node’s left child (or
null if there is no left child), a link to the node’s right child (or null if
there is no right child), and an information field. As with linked lists, this
field will, in practice, be a reference to some object but, for demonstration
purposes, we will simply store integers in our info fields. To start our Node
class, we give it the required fields and a simple constructor:
class Node
{
int info;
Node lChild, rChild;
Objects of the Node class will be represented by diagrams like the following:
Node
info
lChild rChild
Example 2
Using the Tree and Node classes, the implementation of the binary tree
shown in the previous example would have the structure shown in the next
diagram. The nodes are labelled with the same letters used in the previous
example.
506 CHAPTER 12. DYNAMIC DATA STRUCTURES
Tree
root
?
Node
A info
43
lChild rChild
? ?
Node Node
B info C info
27 61
lChild rChild lChild rChild
? ? ? ?
Node Node Node Node
D info E info F info G info
12 38 52 75
lChild rChild lChild rChild lChild rChild lChild rChild
? ?
Node Node
H info I info
20 65
lChild rChild lChild rChild
Root
' $
' $
@
@
& %
& %
12.5. BINARY TREES 507
The names are derived from the point at which the root is visited
relative to the traversal of the two subtrees: pre (before), in (between), or
post (after) the subtrees.
A recursive algorithm for an inorder traversal of a binary tree could
take the following form.
Example 3
Suppose that a binary tree is implemented using the data structure in the
previous example. To print the info fields of such a tree using an inorder
traversal, we could use the following methods.
First, in the Tree class,
public void inPrint ()
{
if (root != null)
root.inPrint();
}
Then, in the inner Node class,
void inPrint ()
{
if (lChild != null)
// print non-empty left subtree
lChild.inPrint();
System.out.println(info);
if (rChild != null)
// print non-empty right subtree
rChild.inPrint();
}
Exercises 12.5
1. For the binary tree shown in the diagram, identify each of the follow-
ing.
(a) the root of the tree
(b) the leaves of the tree
(c) the nodes of the left subtree
(d) the children of the root of the right subtree
(e) a pair of siblings
12.5. BINARY TREES 509
(f) the nodes of the left subtree of the right subtree of the root
(g) a node with no parent
(h) the left child of the root of the left subtree
A
Z
Z
Z
B C
J J
J J
D E F G
J
J J
J
H I J K
2. In what order would the nodes of the following tree be visited using
64
Z
Z
Z
52 39
J J
J J
15 32 26 35
28 20
+
Z
Z
Z
÷ −
J J
J J
8 2 × 6
J
J
3 8
4. The following method for the Tree class starts the process of finding
the sum of the info fields of a binary tree of the type discussed in
this section.
(a) Write the definition of a recursive method sum for the Node class
that will complete the process.
12.6. BINARY SEARCH TREES 511
A binary search tree is a binary tree in which the nodes are arranged using
some key contained in each node. If each key is unique, then we can define
the structure of the ADT binary search tree as: a binary tree in which the
keys of nodes in the left subtree are all less than that of the root, the keys
of nodes in the right subtree are greater than that of the root, and both
subtrees of the root are binary search trees. (If keys are not unique, it is
easy to make appropriate changes to the definition.)
As its name suggests, the structure of a binary search tree allows us
to search easily for an item stored in the tree. We show how to do so in
the next example.
Example 1
Consider the following binary search tree in which names are used as keys.
Humie
HH
H
H
Angela Philip
A A
A A
Alice Dan Mary Thomas
A A
A A
Betsy Dennis Michael
To search for Betsy in this tree, we begin by looking at the root, comparing
Betsy to Humie. Since Betsy precedes Humie, and all keys that are less
than Humie are in the left subtree, we move to the left subtree. We continue
the process by comparing Betsy to Angela and moving to Angela’s right
subtree, comparing Betsy to Dan and moving to Dan’s left subtree, where
we finally find Betsy.
512 CHAPTER 12. DYNAMIC DATA STRUCTURES
If we attempted a search for an item that was not in the tree, then the
process outlined here would eventually reach a dead end by following a
path to an empty subtree. For example, a search for Tina would lead us
to Humie, Philip, and Thomas. Since the right subtree from Thomas is
empty, the search ends in failure.
Example 2
This non-recursive method, for the Tree class, starts insertion by creating
a new node containing the item to be inserted and either inserting it into
an empty tree or calling a recursive method to insert it into the non-empty
tree. The method uses a constructor from the Node class, not shown here.
public void insert (int item)
{
Node newNode = new Node(item,null,null);
if (root == null)
root = newNode;
else
root.insert(newNode);
}
This recursive method inserts a new node into a non-empty binary search
tree.
void insert (Node newNode)
{
if (newNode.info < info)
if (lChild == null)
lChild = newNode;
else
lChild.insert(newNode);
else
if (rChild == null)
rChild = newNode;
else
rChild.insert(newNode);
}
Exercises 12.6
1. In our definition of a binary search tree, we required that each node
have a unique key. Suggest a modification of the definition that could
be used if keys in nodes are not necessarily unique.
Avoiding Errors
1. Always check that your algorithms work for special cases such as
empty lists and lists that have only one node. These can often cause
problems because they may have to be handled slightly differently
from other cases.
2. It is usually a good idea to avoid use of references with identifiers
like current.link.link. If you have to look more than one place
along a list, it is almost always better to use auxiliary variables with
descriptive identifiers such as previous or next.
Debugging
Exercises 12.7
1. The following fragment is intended to print the contents of a linked
list like the ones discussed in this chapter. What is wrong with the
fragment?
2. Write a constructor for the Tree class discussed in this chapter. The
constructor should have three int parameters: a, b, and c. It should
construct the tree shown in the diagram.
a
J
J
b c
3. Write a fragment that will create a new node containing the value 27
in its info field and then insert this node into the list between the
two nodes shown in the diagram.
current
?
Node Node
info 30 info 25
- -
link link
12.8. REVIEW EXERCISES 12 517
A
Z
Z
Z
Z
B C
J J
J J
D E F G
J
J
H I
1 Unless stated otherwise, assume in these exercises that lists and trees are defined
as we have throughout this chapter. You may also assume that the Node classes contain
constructors like those that we have used in the text.
518 CHAPTER 12. DYNAMIC DATA STRUCTURES
J
J
1 3
4. Draw a diagram showing the resulting binary search tree if the items
shown here are inserted in the given order into an initially empty
tree.
?
Node
info
A
lChild rChild
B
? ?
Node Node
info info
C E
lChild rChild lChild rChild
D F
7. Write an instance method append that has one int parameter, item.
The method should attach a new node containing item in its info
field onto the end of the method’s implicit List object.
10. Write an instance method reverse that could be used in the class
List. The method should reverse the order of the nodes of its implicit
List object.
12. Assuming that linked lists are maintained with their info fields in
strictly ascending order with no duplicate entries, write a method for
the List class with the header
public void printMerged (List other)
The method should print, in ascending order, the values contained in
the union of both lists, one value per line. You may assume that the
lists have no elements in common.
13. (a) Write instance methods called nodeCount that, working together,
return the number of nodes in a binary tree.
(b) Write instance methods called leafCount that will count the
number of leaves in a binary tree.
14. Suppose that we define the level of a node in a binary tree as the
number of edges on the path from the node to the root. For example,
in the binary tree shown in the diagram, the levels of A, B, C, and D
are, respectively, 0, 1, 1, and 2.
520 CHAPTER 12. DYNAMIC DATA STRUCTURES
A
J
J
B
C
J
J
D
We can then define the height of a binary tree as the maximum level
of any node in the tree. Thus the height of the binary tree shown
above is 2. Using this definition of height, write definitions for a pair
of methods for the Tree and Node classes that return the height of a
binary tree. If the tree is empty, the value −1 should be returned.
15. (a) Write instance methods that will print the contents of a binary
tree in parenthesized form as follows:
(<item>:<left subtree>,<right subtree>)
Here <item> is the root’s info field, <left subtree> is the con-
tents of the left subtree printed in parenthesized form and <right
subtree> is the contents of the right subtree, printed in paren-
thesized form. An empty tree should produce no output. Using
this form, the contents of the tree
2
J
J
5 4
J
J
9 7
Projects
then printing the values in the tree using an inorder traversal. Your
program should repeatedly prompt the user for input, stopping when
the user indicates that all input has been provided.
Example 1
The following program creates and displays a window that contains a greet-
ing to the world.
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Container;
import javax.swing.JComponent;
import java.awt.Graphics;
class GUIGreet
{
public static void main (String[] args)
{
JFrame frame = new JFrame("Graphical Greeting");
frame.setSize(400,100);
1 The illustrations shown in the text were produced by programs running on a Win-
dows operating system. If you are running your programs on some other platform, your
windows may have a slightly different appearance.
13.1. DRAWING TEXT 525
If you run this program, it should produce a window like the following:
This window can be resized, dragged around the screen, minimized, and so
on — just like any other window in your system.
As you can see, this program contains many new features of the lan-
guage. Let us now take a look at these new items.
1. import javax.swing.JFrame;
As we have said before, Java’s classes are organized into units called
packages. Up to now, the Java classes that you have been dealing
with may all have been in the package java.lang, the core package
of the language. Here, however, we need classes from other packages.
The five import statements at the beginning of the program tell the
526 CHAPTER 13. GRAPHICAL USER INTERFACES
4. frame.setSize(400,100);
The method setSize, as the name implies, sets the dimensions of
the window that will be displayed when the program starts running.
In drawing to a screen, the unit of measure is the pixel, the smallest
unit of resolution for that screen. Our window is 400 pixels wide and
100 pixels high.
6. pane.add(new Greeting());
Now, we construct a new Greeting object and add it to the pane
of the window using the add method of the Container class. (The
class JPanel, as we noted above, inherits from Container so JPanel
objects can use Container methods.)
13.1. DRAWING TEXT 527
7. frame.setVisible(true);
A call to the setVisible method with the argument true is required
to enable us to see the image.
8. class Greeting extends JComponent
The class Greeting is defined by extends to be a subclass of the
class JComponent in the package javax.swing. Therefore it inherits
all of that class’s methods. The inherited method that we will be
using in this program is paint.
9. public Greeting ()
This constructor of the Greeting class simply calls the repaint
method which in turn calls the paint method to draw the image. We
do not call paint directly but, instead, put in a request for painting
(through the call to repaint) and let Java determine the appropriate
time to actually perform the painting (by calling paint).
10. public void paint (Graphics g)
This method overrides the paint method of the class JComponent so
that it will paint whatever we want on our component. The param-
eter of the method is a Graphics object that is created by Java and
assigned to g when the paint method is called. The object g stores
the properties (such as colour, font size, and so on) associated with
the component’s area of the screen. The paint method is called by
Java whenever the application should be drawn (or redrawn) onto
the screen.
11. g.drawString("Hello, world",150,50);
This statement uses the drawString method from the Graphics class
to draw the contents of a string. The last two arguments give the co-
ordinates (in pixels) of the left end of the baseline of the first character
in the string. The first coordinate gives the horizontal distance of this
point from the left side of the area while the second coordinate gives
the vertical distance from the top. Here, then, the message will start
150 pixels from the left, with its base 50 pixels below the top. It is,
therefore, roughly in the centre of the component.
When we run the program, the main method executes and terminates
but the window stays on the screen until we close it with our mouse. What
is happening here is an example of multi-threading. Up to now, our pro-
grams have executed code and, once they have finished that task, they have
stopped automatically. When we create a window, however, this starts the
528 CHAPTER 13. GRAPHICAL USER INTERFACES
Example 2
The following code, inserted into the main method of Example 1, will cause
the program to terminate when the window defined by frame is closed. A
program using this code must also import the class WindowEvent in the
package java.awt.event. We can do this by writing
import java.awt.event.WindowEvent;
at the beginning of the program.
frame.addWindowListener
(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
}
);
The size and style of the text displayed in a window can be con-
trolled using the class java.awt.Font. To change to something other than
the default font, we must specify a font name, a style, and a size. We
13.1. DRAWING TEXT 529
Example 3
The following fragment could be used in a paint method to draw the word
tiny.
Example 4
Here is the code for our revised version of our greeting to the world.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
class RevisedGreet
{
public static void main (String[] args)
{
JFrame frame = new JFrame("Big Greeting");
frame.addWindowListener
(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
}
);
frame.setSize(400,100);
JPanel pane = (JPanel) frame.getContentPane();
pane.add(new BigGreet());
frame.setVisible(true);
}
}
Exercises 13.1
1. How would you set the title of a window to be Sample?
5. Write a program that will draw your name and address in a window,
as they would appear on an envelope. Try to have the output appear
near the centre of the content pane.
532 CHAPTER 13. GRAPHICAL USER INTERFACES
Although Java has a large number of very powerful methods for creating
and manipulating images, most of these are well beyond the scope of this
book. We will be examining only a tiny fraction of the methods — those
that can be used to create simple shapes in solid colours. To create our
drawings, we will be using methods from the class java.awt.Graphics,
just as we did in the last section, where we were drawing text. As was the
case in drawing text, the coordinate system has its origin in the upper left
corner of the drawing area and the coordinates of a point are the distances
in pixels to the right of and below the origin.
One of the simplest geometric objects that we can draw is a line seg-
ment. Although it is possible to draw line segments of various thicknesses
and characteristics (wide, thin, dotted, dashed, rounded ends, and so on),
we will only be drawing segments that are one pixel wide. To do so, we
use the method drawLine in which the four int parameters represent the
coordinates of the ends of the segment.
Example 1
The statement
g.drawLine(20,30,400,300);
will draw a line segment from the point with coordinates (20,30) to the
point with coordinates (400,300).
Example 2
The following fragment draws a solid 40 × 40 square in the upper left hand
corner of the drawing region and the outline of a rectangle that is 100 pixels
13.2. DRAWING SIMPLE SHAPES 533
wide and 20 pixels high located just below and to the right of the square.
g.fillRect(0,0,40,40);
g.drawRect(40,40,100,20);
The illustration shows a window containing these figures. The window is
400 pixels wide and 100 pixels high.
We can also draw ellipses and circles either in outline using drawOval
or filled using fillOval. These methods also have four int parameters:
the first two specify the coordinates of the point at the upper left corner
of a rectangle that would just contain the figure while the last two give the
width and height respectively.
Example 3
The following fragment first draws the outline of a rectangle and then draws
the outline of an ellipse contained by the rectangle. Finally, it draws a filled
circle inside the ellipse. The results are again displayed in a 400 × 100 pixel
window.
g.drawRect(100,10,200,50);
g.drawOval(100,10,200,50);
g.fillOval(175,10,50,50);
Notice the horizontal coordinate of the left end of the circle. Since the left
end of the ellipse is at 100 and the ellipse is 200 pixels wide, its centre is
located 100 +200 ÷2 = 200 pixels from the left. For the circle to be centred
on the ellipse, its centre must also be 200 pixels from the left. The circle’s
diameter is 50 pixels so the left side of the circle is located 25 pixels (the
circle’s radius) to the left of the centre of the ellipse. Thus the circle starts
534 CHAPTER 13. GRAPHICAL USER INTERFACES
Example 4
The following fragment draws an open triangle and a filled quadrilateral.
int[] xTri = {50,150, 50};
int[] yTri = {20, 60, 60};
g.drawPolygon(xTri,yTri,3);
If none of these colours is suitable, you can construct your own colours
in a variety of ways. One such way is to use a constructor of the Color
class in which the RGB values are the arguments of the constructor.
Example 5
The following statement creates a Color object representing lime green.
java.awt.Color lime = new java.awt.Color(128,255,0);
Of course, by using a suitable import statement, this can be abbreviated
to
Color lime = new Color(128,255,0);
Example 6
The following method could be used to draw a solid magenta ellipse in the
top left corner of a window with a lime green label on top of the ellipse.
g.setColor(Color.magenta);
g.fillOval(0,0,100,50);
Exercises 13.2
1. If a region is 300 pixels wide and 200 pixels high, determine the
coordinates of each point.
4. Write a paint method that could be used to draw the following pat-
tern of squares. The centre of the smallest square has coordinates
13.3. LAYOUT MANAGERS 537
If a container has more than one component, the position and size of each
of these components can be controlled by a layout manager. Swing has a
number of different layout managers and even allows you to create your
own if you want to do so. Here, however, we will examine only three.
Before we start to examine layout managers, we need something to
arrange. An object that is simple to create and display is a button, a region
of the screen that can be “pressed” by clicking on a mouse. Java has more
than one class of button; our buttons are going to be objects constructed
from the JButton class in the package javax.swing. In this section, we
will only be creating and arranging buttons. We defer our discussion of
how to use them until the next section.
538 CHAPTER 13. GRAPHICAL USER INTERFACES
FlowLayout
The objects of Java’s simplest layout manager are constructed from the
class FlowLayout in the package java.awt. With FlowLayout, components
are arranged rather like words are arranged by a word processor using a
centred alignment — from left to right, and from top to bottom, with each
row centred between the left and right sides of the container. To construct
a FlowLayout in a JPanel object called pane, we could write
pane.setLayout(new FlowLayout());
Once we have a manager for a JPanel we can add components using the
instance method add of the class JPanel. For example, to add a button
with the label "Help" to the JPanel object called pane, we could first
construct a new button by writing
JButton helpButton = new JButton("Help");
and then add it to our panel by writing
pane.add(helpButton);
In this section, because we are not doing anything with our buttons, we
have no need for button identifiers. We will, therefore, abbreviate the code
for creating and adding a button to something like
pane.add(new JButton("Help"));
Example 1
This program uses a FlowLayout manager to create and arrange five but-
tons in a window.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
class FlowDemo
{
public static void main (String[] args)
{
JFrame frame = new JFrame("Flow Layout");
frame.addWindowListener
(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
13.3. LAYOUT MANAGERS 539
System.exit(0);
}
}
);
frame.setSize(400,100);
JPanel pane = (JPanel) frame.getContentPane();
pane.setLayout(new FlowLayout());
pane.add(new JButton("A"));
pane.add(new JButton("B"));
pane.add(new JButton("C"));
pane.add(new JButton("D"));
pane.add(new JButton("E"));
frame.setVisible(true);
}
}
The output of the program is shown in the next illustration. Any but-
ton that we create has a “preferred” size — enough space for the label plus
a border. A FlowLayout manager uses this information to size and position
the buttons. Here the buttons are arranged in a single row, centred at the
top of the window. The order in which we added the buttons determines
the order in which they appear in the window.
BorderLayout
The second layout manager that we will examine is BorderLayout.
Here, the panel is divided into five regions identified by the constants NORTH,
SOUTH, EAST, WEST, and CENTER in the BorderLayout class. To add a
component, we use a version of the overloaded method add that has two
parameters: the component to be added and its position. NORTH and SOUTH
occupy the entire top and bottom of the area, EAST and WEST occupy the
remaining right and left sides, and CENTER takes whatever is left in the
interior.
Example 2
This program creates five buttons and places one in each of the five regions
of a BorderLayout.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
class BorderDemo
{
public static void main (String[] args)
{
JFrame frame = new JFrame("Border Layout");
frame.addWindowListener
(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
13.3. LAYOUT MANAGERS 541
{
System.exit(0);
}
}
);
frame.setSize(300,200);
JPanel pane = (JPanel) frame.getContentPane();
pane.add(new JButton("Center"),BorderLayout.CENTER);
pane.add(new JButton("West"),BorderLayout.WEST);
pane.add(new JButton("East"),BorderLayout.EAST);
pane.add(new JButton("North"),BorderLayout.NORTH);
pane.add(new JButton("South"),BorderLayout.SOUTH);
frame.setVisible(true);
}
}
The resulting window is shown in the following diagram. Notice that the
buttons, rather than being displayed at their preferred sizes, are sized to fill
each region. If we were to resize the window, the regions (and the buttons)
would expand or contract as required to keep the window filled. If the
window is made too small, then one or more of the regions may not be
visible but they will not be destroyed. If no component is added to one of
the perimeter regions, the other regions will expand to occupy that space.
542 CHAPTER 13. GRAPHICAL USER INTERFACES
GridLayout
The last layout manager that we will be looking at is called Grid-
Layout. With this manager, we specify the number of rows and columns in
which the components are to be placed. The components are all the same
size and their dimensions are set so that the full width and height of the
container are utilized.
The constructor for the GridLayout has a parameter list of the form
(int row, int col). Normally, Java only pays attention to the row spec-
ification, creating as many columns as necessary to fit the components into
the region. If the value of row is greater than the number of components,
blank space is left at the bottom of the region. If the value of row is zero,
the layout manager pays attention to the value of col and creates a grid
layout with the specified number of columns and the necessary number of
rows (if there are enough components for the specified dimension).
Example 3
This program displays five buttons in a grid containing three rows and two
columns.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
class GridDemo
{
public static void main (String[] args)
{
JFrame frame = new JFrame("Grid Layout");
frame.addWindowListener
(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
}
);
frame.setSize(200,100);
13.3. LAYOUT MANAGERS 543
Since there are five components and we have specified that the grid should
contain three rows, the resulting window contains two columns, only the
first of which is entirely filled. The fact that we specified two columns in
the GridLayout constructor is irrelevant. If we had used (3,0) or (3,10)
rather than (3,2), we would have produced the same display.
Notice the order in which the components have been placed in the
window — row by row (sometimes called row major order ).
Java has other layout managers but, even with the ones that we have
discussed, complex layouts can be produced because we can nest layouts of
any type within other layouts of any type.
Example 4
The following program illustrates a mixture of all three types of layouts
that we have examined. In it, we have embedded both a BorderLayout
and a FlowLayout within a GridLayout.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
544 CHAPTER 13. GRAPHICAL USER INTERFACES
class ComboDemo
{
public static void main (String[] args)
{
JFrame frame = new JFrame("Combo Layout");
frame.addWindowListener
(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
}
);
frame.setSize(500,200);
JPanel pane = (JPanel) frame.getContentPane();
pane.setLayout(new GridLayout(2,2));
pane.add(new JButton("A"));
JPanel borderPane = new JPanel();
borderPane.setLayout(new BorderLayout());
borderPane.add(new JButton("B-Center"),BorderLayout.CENTER);
borderPane.add(new JButton("B-West"),BorderLayout.WEST);
borderPane.add(new JButton("B-East"),BorderLayout.EAST);
borderPane.add(new JButton("B-South"),BorderLayout.SOUTH);
pane.add(borderPane);
JPanel flowPane = new JPanel();
flowPane.setLayout(new FlowLayout());
flowPane.add(new JButton("C-1"));
flowPane.add(new JButton("C-2"));
flowPane.add(new JButton("C-3"));
flowPane.add(new JButton("C-4"));
flowPane.add(new JButton("C-5"));
flowPane.add(new JButton("C-6"));
flowPane.add(new JButton("C-7"));
pane.add(flowPane);
pane.add(new JButton("D"));
frame.setVisible(true);
}
}
13.3. LAYOUT MANAGERS 545
Example 5
The following program displays a solid blue ellipse centred in a region
between two buttons.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
class BorderDemo
{
public static void main (String[] args)
{
JFrame frame = new JFrame("Graphics & Buttons");
frame.addWindowListener
(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
546 CHAPTER 13. GRAPHICAL USER INTERFACES
}
}
);
frame.setSize(200,200);
JPanel pane = (JPanel) frame.getContentPane();
pane.add(new Picture(),BorderLayout.CENTER);
pane.add(new JButton("Start"),BorderLayout.NORTH);
pane.add(new JButton("Stop"),BorderLayout.SOUTH);
frame.setVisible(true);
}
}
Notice the way that the elliptical region is created and displayed. In
the main method, we wrote
pane.add(new Picture(),BorderLayout.CENTER);
This calls the constructor in the Picture class. The constructor simply
calls repaint which, as we have noted previously, initiates a request to
Java to call the paint method of the current class whenever it is convenient.
The paint method, when called by Java, creates the elliptical region.
Exercises 13.3
1. Suppose that a grid layout is to be used to display fourteen buttons.
How many rows and columns would be displayed in each case if the
call to the grid layout constructor had the form shown?
(a) new GridLayout(3,5) (b) new GridLayout(5,4)
4. Write a program that will display two solid red circles in a window.
The diameter of each circle should be one quarter of the smaller of
the width and height of the region. One circle should be centred
in the upper left quadrant while the other should be centred in the
lower right quadrant. Resizing the window should cause the circles
to resize appropriately.
the event. They can then act on this information in whatever way that we
wish them to do.
To begin to show how this works, suppose that we have constructed a
JButton object called demoButton and that we want an object of the class
ButtonResponse to act as a listener for presses of demoButton. To do so,
we must do a number of things.
1. We register an object as a listener by implementing an interface. To
listen to a button, the interface that we need to implement is called
ActionListener. For our example, we note that ButtonResponse
is implementing the ActionListener interface by writing the class
header as
class ButtonResponse implements ActionListener
The next example shows how we can incorporate these ideas into a
complete (although fairly trivial) program. Despite its small size, the pro-
gram is modular with the class containing the main method simply creating
a frame for the GUI. All the other functionality of the program is in a sep-
arate class.
550 CHAPTER 13. GRAPHICAL USER INTERFACES
Example 1
This program creates a window containing a single button. Whenever the
button is pressed, the colour of the panel containing the button changes
randomly.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public ColourFrame ()
{
super("Button Events");
pane.add(colourButton);
setSize(300,100);
setVisible(true);
}
These statements create a button and also add the current Colour-
Frame object to the list of listeners to events produced by presses of
colourButton.
4. setSize(300,100);
setVisible(true);
Finally, the ColourFrame constructor sets an initial size for the win-
dow and makes it visible.
Example 2
The class TriColourPane defines windows that have three buttons — one
for each of the component colours (red, green, or blue). An object of this
class will set the background colour of a window to one of these colours
whenever the appropriate button is pressed. We have not shown a class
13.4. BUTTON EVENTS 553
public TriColourFrame ()
{
super("Tricolour Buttons");
setSize(300,100);
setVisible(true);
}
Exercises 13.4
1. In Example 2, we said that we were omitting a main method be-
cause “it would be virtually identical to the one shown in Example
1”. What slight difference would there be in the main methods of
Example 1 and Example 2?
4. Modify the program of the previous question so that the labels on the
buttons are "Brighter" and "Dimmer". If a user presses "Brighter",
the background colour should be set closer to white (if it is not already
white) while pressing "Dimmer" moves the background closer to black
(if it is not already black). Have each press of a button change the
intensity by one-sixteenth of the difference between pure white and
pure black.
Mouse events are handled in ways similar to those used with button events.
Mouse events, however, are slightly more complex because there are many
things that we can do with a mouse while the only thing that we can do
with a button is press it.
To use a mouse, we again register a class as a listener. In order to make
efficient use of system resources, there are two kinds of listener interfaces
associated with mouse events: MouseListener and MouseMotionListener.
Registration of a class as a listener takes a form similar to that used with
button events. Implementation of the MouseListener interface requires
the implementation of all of the following five methods.
• public void mousePressed (MouseEvent e)
This method is called when a mouse button is pressed within the
component to which we are listening.
556 CHAPTER 13. GRAPHICAL USER INTERFACES
Example 1
The following program will draw a filled circular region of radius 10 pixels
at any point at which the mouse is clicked (within the window created by
the program).
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
private int y;
private static final int RADIUS = 10;
public CircleFrame ()
{
super("Mouse Droppings");
addMouseListener(this);
setSize(300,100);
setVisible(true);
}
Here is a typical image produced by the program. Each click of the mouse
within the window produces a new circle centred at the point of the click.
Example 2
The following code shows how we could change the program in Example 1
to avoid the need for writing stub methods in a class acting as a listener.
private int x;
private int y;
private static final int RADIUS = 10;
public CircleFrame ()
{
super("Mouse Droppings");
addMouseListener(new ClickHandler());
setSize(300,100);
setVisible(true);
}
Example 3
The following code uses an anonymous inner class to install a mouse listener
in the class CircleFrame.
public CircleFrame ()
{
super("Mouse Droppings");
addMouseListener(new MouseAdapter()
{
public void mouseClicked(MouseEvent e)
{
x = e.getX();
y = e.getY();
repaint();
}
}
);
setSize(300,100);
setVisible(true);
}
If all of this is clear, then you should now be able to understand what
is going on whenever we want to have the closing of a window terminate a
program. Let us examine the code that we have been using.
frame.addWindowListener
(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
}
);
Here the anonymous inner class extends the WindowAdapter class which
contains trivial versions of all seven methods required to implement the
WindowListener interface. The only one of these methods that we want
to override is windowClosing which we rewrite so that it executes the
statement System.exit(0); causing the program to halt. The argument
of zero signifies normal termination. Simple, eh?
Exercises 13.5
1. Explain the difference between the terms.
3. Write a program that first allows the user to press the mouse at one
point on the screen, move the mouse (keeping the button pressed) to
another point, and then release the mouse. Once the user has done
this, the program should draw the line segment that connects the two
points.
4. Write a program that allows the user to click on two points and
then draws the circle that has its centre at the first point and passes
through the second point.
5. Write a program that allows the user to make simple drawings using
the mouse. The program should adapt the examples shown in this
section so that small circles are drawn whenever the mouse is being
dragged across the component. Sample output (using circles with a
radius of five pixels) is shown in the following diagram.
6. Write a program that allows the user to click on two points and then
draws pixel-sized dots at the clicked points and a portion of the right
bisector of the line segment joining the points.
Although Swing provides many powerful tools for handling text, we will,
as usual, only look at a very small subset. Our attention will be limited to
the JTextField class which allows us to read and write single lines of text.
A JTextField object appears to a user as an area in which text can
(normally) be typed as it would in any single-line text editor in which text
can be entered and edited (by backspacing, inserting, or deleting).
13.6. TEXT INPUT AND OUTPUT 565
Example 1
To create a JTextField object called inField, we could write
JTextField inField = new JTextField(20);
This would create a field whose appearance would be about wide enough
to hold at least twenty of the widest characters in the current font (if the
field is displayed at its preferred size).
The size specified in the constructor does not affect the number of
characters that can by typed in the field, only the number that we can see
at one time. If the size of a window is changed, the size of any JTextField
may be changed by the layout manager. You can, in fact, leave all display
size decisions up to the layout manager by using a constructor with no
argument.
JTextField objects behave in many ways like JButton objects. Both
generate event objects from the class ActionEvent — a JButton object
generates its events when it is pressed while a JTextField object generates
its events when the user presses the <enter> key while the cursor is in the
text entry area. To register as a listener to JTextField events, we must
do as we did for JButton events: implement the ActionListener interface
by writing an actionPerformed method.
To determine which button was the source of an action event, we used
an action command. Recall that the default action command of a JButton
was the string that labelled the button. If we need an action command for
a JTextField, we can create one when we construct the field by using a
slightly different constructor. For example, if we wanted inField to have
the string "Input" as its label, then we could construct inField by writing
JTextField inField = new JTextField("Input",20);
Example 2
To create a field that can only be used to display output, we could write
JTextField outField = new JTextField("result",10);
outField.setEditable(false);
566 CHAPTER 13. GRAPHICAL USER INTERFACES
Example 3
To convert the JTextField object inField to a string, we could write
String inString = inField.getText();
inString = inString.trim();
Taking advantage of Java’s facility for chaining methods, we usually ab-
breviate this to
String inString = inField.getText().trim();
Example 4
If a JTextField object called inField represents an integer, we could
extract that value by writing
int inValue = Integer.parseInt(inField.getText().trim());
which first converts the field to a string, then trims off any leading or
trailing white space, and finally converts the trimmed string value to an
integer.
Example 5
To display the double value result in the JTextField called outField,
we could write
outField.setText(String.valueOf(result));
As an alternative to using the valueOf method to convert result to a
string, we could simply concatenate result onto an empty string.
outField.setText("" + result);
By default, values are written left-justified in the display area.
Example 6
To create a display area showing the text Age in years: and add it to a
panel called inputPane, we could write
inputPane.add(new JLabel("Age in years:"));
The result would be an area like the following.
The next example gives a simple illustration of both input and output
of text, the use of labels, and a button to control the display of the output.
The example also illustrates the use of the method pack which requests the
layout manager to display components at their preferred sizes rather than
at some size specified by us using setSize.
Example 7
The program shown here allows a user to supply a given name and a family
name into separate JTextField objects. Whenever the user presses a but-
ton to submit the component names, the program displays the combined
name in a third JTextField.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
System.exit(0);
}
}
);
}
}
public NameFrame ()
{
super("Text Fields");
submitPane.setLayout(new FlowLayout());
submitPane.add(new JLabel("Press Button to Enter Names"));
JButton submitButton = new JButton("Submit");
submitButton.addActionListener(this);
submitPane.add(submitButton);
pane.add(submitPane,BorderLayout.CENTER);
Notice that the non-editable output field is displayed differently from the
editable input fields.
As we have stated many times, there are far more features in Swing
than we have space to cover. For those interested in exploring further,
there are many resources available. One book that provides a clear and
much more thorough treatment is Learning Java by Patrick Niemeyer and
Jonathan Knudsen, published by O’Reilly.
Sun’s Java web site, located at http://java.sun.com is another source
of assistance. There you will find, among other things, thorough documen-
tation of all classes in the API. At first you may find this resource to be
quite intimidating as it often tells you far more than you might ever want
to know but those who persist usually find it to be extremely useful.
Exercises 13.6
1. What would you have to do to prevent a user from using a JText-
Field called result for input?
calculating 7 × 5.
Avoiding Errors
1. If you change the contents of a window, be sure to redraw it. To
do so, call the repaint method, not the paint method. If you call
repaint, the window manager will automatically call paint at an
appropriate time.
Debugging
(a) g.setColor(Color.Red));
(b) g.setFont("Serif");
2. Write a program that will display a window that is 500 pixels wide
and 400 pixels high. The window should contain the following, drawn
as indicated:
3. Write a program that allows the user to click on two points within
a blank window and then draws the rectangle that has those points
as the endpoints of one of its diagonals. If the points are aligned
vertically or horizontally, the program should simply join the two
points with a line segment.
Projects
5. Write a program that asks the user to submit the time in hours
and minutes in two text fields. The program should then draw a
574 CHAPTER 13. GRAPHICAL USER INTERFACES
6. Write a program that first presents the user with a window that
contains a blank square area with labelled text at the bottom. The
label should direct the user to enter an integer value n in the range 2 ≤
n ≤ 100. After the user enters the number and presses <enter>, the
program should draw n filled circles, each of radius four pixels. These
circles should be arranged at equal intervals around the perimeter of
a large circle. The diameter of the large circle should be determined
by the size of the region; it should have a diameter equal to 90% of
the lesser of the width and height of the region.
7. Although we can draw a filled circle fairly easily using the fillOval
method, this question asks you to draw such a figure the hard way,
one pixel at a time. Specifically, you are to draw the graph of the
relation x2 + y2 ≤ 4 (a circular disk with radius 2 and centre at
the origin). In making your drawing, position the point (0, 0) at the
centre of the display area and try using a scale of one unit = 200
pixels. Set the scale by defining a constant:
final int SCALE = 200; // pixels per unit
This should produce a drawing that will fit nicely on the screen of
your computer but, if it does not, write the program so that you
can change the size of the image easily by changing the value of the
constant.
To draw the disk, for each pixel corresponding to a point in the
square region in which −2 ≤ x ≤ 2 and −2 ≤ y ≤ 2, test whether
the sum of the squares of the coordinates of the point is less than or
equal to 4. If so, the point lies in the disk. If the point lies in the
disk, fill in the pixel by invoking the method fillOval with a width
and height of 1.
8. Modify the program of the previous question to draw the graph of the
Mandelbrot set. This is a set of points on an Argand diagram, a coor-
dinate system in which the complex number z = a + ib is represented
by the point (a, b). To determine whether or not a complex number
c is in the Mandelbrot set, we examine the terms of the following
13.8. REVIEW EXERCISES 13 575
sequence.
z1 = c
zk = zk−1 2 + c, if k > 1
10. Modify the program in the previous question so that a user can play
against the computer. Try to make the computer’s moves reasonably
intelligent.
11. The game called Life was invented in 1970 by the British mathe-
matician John Conway. It is played by one person on a (theoreti-
cally) unbounded grid of squares. Each cell on the grid represents
an organism which can, at any time, be either alive or dead. Which
organisms are alive changes from generation to generation according
576 CHAPTER 13. GRAPHICAL USER INTERFACES
to the number of neighbouring cels that are alive. Births and deaths
take place according to the following rules:
• The neighbours of a given cell are the eight cells that are adjacent
to it vertically, horizontally, or diagonally.
• If a cell is alive but has only zero or one neighbours that are
alive, then in the next generation the cell dies of loneliness.
• If a cell is alive and has four or more neighbours that are alive,
then in the next generation the cell dies of overcrowding.
• A living cell with either two or three living neighbours remains
alive in the next generation.
• If a cell is dead, then in the next generation it will become alive
if it has exactly three neighbours that are already alive in this
generation.
• All births and deaths take place at exactly the same time so that
dying cells can help give birth to other cells but cannot prevent
the deaths of others by reducing overcrowding. Similarly, cells
about to be born can neither preserve nor kill cells living in
the current generation. In order to follow this rule, one must
determine all births and deaths before creating or destroying
any cells.
Write a program that will play the game of Life on a bounded grid.
The program should allow the user to specify a starting pattern and
should then continue to create new generations periodically until the
user stops the action. The user should be able to control the time be-
tween generations. Java’s Timer and TimerTask classes in the pack-
age java.util provide methods for controlling time between events.
Despite the simplicity of the rules of the game, many quite inter-
esting patterns and dynamics are possible. Some of the “creatures”
composed of collections of living cells can blink, glide, and even pro-
duce offspring. For some of the possibilities, take a look at some of
the many web sites devoted to this subject or see the book Wheels,
Life and Other Mathematical Amusements by Martin Gardner.
Appendix A
In
The class In contains methods for reading from the standard system input.
A method is provided for each of the data types: int, long, float, double,
char, and String. The methods assume that each input value is terminated
by a newline. If incorrect input is entered, a value of 0 is returned for
numeric input. For the type char, if more than one character is entered, the
first is returned; if only a newline is entered, the character ’\n’ is returned.
For strings, if only a newline is entered, the string "" is returned.
import java.io.*;
import java.text.*;
public class In
{
static InputStreamReader r=new InputStreamReader(System.in);
static BufferedReader br=new BufferedReader(r);
{
// if any exception occurs, just return zero
return new Integer(0);
}
}
Out
The class Out contains print and println methods that give better control
of output than the methods of the same names in Java’s System class.
Given a single argument, the methods simply call the standard methods.
Unlike the standard methods, however, these print and println methods
are overloaded to allow extra arguments that control the format of the
output.
• For floating point values, print and println can be called with three
arguments: the value to be printed, the total field width for the out-
put, and the number of digits to be printed after the decimal point.
Again, output will be right-justified in the field. For example,
Out.println(Math.PI,6,2);
will print
3.14
Once again, the total field width argument actually specifies a mini-
mal field width. If more space is required, it will be given.
• Strings can have a second argument that specifies the field width. If
the string is shorter than the specified width, it will be left-justified
in the output field; if it is longer, excess characters on the right will
not be printed. For example,
Out.print("Example",4);
will print
Exam
import java.text.*;
{
public static void println ()
{
System.out.println();
}
String s = String.valueOf(n);
int stop = fieldWidth - s.length();
for (int i = 0; i < stop; i++)
s = " " + s;
System.out.print(s);
}
At a very basic level, input and output data are treated in Java as streams
of bytes — groups of eight bits with integer values. If we want to read or
write various types of data, we must have ways of converting the form of
our data. We must also have ways of directing the streams so that data are
obtained from the right source (the keyboard or a file) or sent to the right
destination (the screen or a file). Each of the four possibilities is dealt with
in the following sections.
When attempting to perform reading or writing, a variety of things
can go wrong. If they do, then Java may throw an exception. In some of
the examples in this appendix, we show how to write programs that can
produce exceptions but we do not give any explanations. For a discussion
of exceptions, see Appendix E.
Standard Output
We have been sending data to the standard output stream since our first
program. To do so, we simply write a statement of the form
System.out.println(...);
where ... represents the data that we want to print. To dissect this a bit,
we note that System.out is a class field in the System class. It is a reference
to an object of type PrintStream and it refers to the “standard” output
stream — usually the screen. This stream does not have to be opened
by the programmer; it is already ready to accept data. The PrintStream
class contains many versions of the methods println and print, capable
of displaying various forms of data.
Standard Input
Reading from the standard input stream (usually the keyboard) is not
nearly as simple a process as writing to the standard output stream al-
though some features are similar. The System class contains a class field
called in that refers to this stream. The object that System.in refers to is
of type InputStream; it is the “standard” input stream of the system. The
data from the stream come in as integer-valued bytes. If we want to read
strings, we must convert the form of the data. To do so, we use a couple
of wrapper classes that act as converters of the stream. The first step is to
convert the bytes to Java characters (which are two bytes long). We can
do this with an InputStreamReader object as follows.
587
Example 1
This fragment prompts for a string and then reads it from the standard
input stream.
If the input represents something other than a string, we first read the
data as a string and then convert it to whatever type is appropriate. If
the string represents a numeric value, we can extract that value by using a
parsing method. These are class methods that are provided for each type
of numeric value. The table gives the classes and the method headers of
some parsing methods provided in the API.
588 APPENDIX B. INPUT AND OUTPUT
Example 2
The next fragment prompts for and reads a double value from the standard
input stream.
InputStreamReader r = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(r);
System.out.println("Enter mass in kg (to nearest tenth)");
String line = br.readLine();
double mass = Double.parseDouble(line.trim());
Character input can be extracted from the string by using the charAt
method of the String class. If the input string is called line, then the
expression line.charAt(0) will give an appropriate char value.
File Output
There are various ways of writing data to a file. One of the simplest is to use
the FileWriter and PrintWriter classes both of which are in the package
java.io. We can create a FileWriter object that writes characters to an
output stream to the file foo.txt by writing
FileWriter fw = new FileWriter("foo.txt");
If the file does not exist, the constructor creates it; if the file does exist, any
data sent to the file will overwrite the old contents of the file. The Print-
Writer class contains the usual collection of println and print methods
589
that produce text representations of both primitive types and objects. Once
we are finished with a file, we should close the PrintWriter using its close
method. This flushes any data from the buffer and severs any connection
to the file.
Example 3
The program shown here demonstrates the use of a file for output. The
program simply writes each of the strings "Hello" and "world" to the file
c:\samples\greet.txt and then closes the file.
import java.io.*;
class FileOutDemo
{
public static void main (String[] args)
{
String s;
try
{
FileWriter fw=new FileWriter("c:\\samples\\greet.txt");
PrintWriter pw=new PrintWriter(fw);
pw.println("Hello");
pw.println("world");
pw.close();
}
catch(IOException e)
{
...
}
}
}
Notice the use of double backslashes (\\) in the string specifying the name
of the file. These are necessary because \ is Java’s escape character.
File Input
We can create a stream that reads from a file by constructing an object
of the FileReader class (found in the package java.io). If the input file
590 APPENDIX B. INPUT AND OUTPUT
is called foo.txt, then we could create an input stream for this file by
writing
FileReader fr = new FileReader("foo.txt");
We can increase efficiency, as we did with input from the standard input
stream, by wrapping this stream in a BufferedReader class that buffers
the characters from the FileReader. This is done by writing
BufferedReader br = new BufferedReader(fr);
Again, as we did with input from the standard input stream, we can com-
bine these operations into one statement.
BufferedReader br
= new BufferedReader(new FileReader("foo.txt");
Once we have constructed a BufferedReader, we can read from a file using
the readLine method of the BufferedReader class. If the end of the file
is reached, readLine returns the value null.
Example 4
The program shown here demonstrates the use of a file for input. The
program repeatedly reads strings from the file c:\projects\results, con-
verting each one to an integer and adding its value to a running sum of all
the values read. Once the end of the file has been reached, the program
closes the file and prints the sum.
import java.io.*;
class FileInDemo
{
public static void main (String[] args)
{
try
{
FileReader fr=new FileReader("c:\\projects\\results");
BufferedReader br=new BufferedReader(fr);
String line;
int sum = 0;
while ((line = br.readLine()) != null)
sum += Integer.parseInt(line);
System.out.println("Sum is: " + sum);
br.close();
}
catch(IOException e)
591
{
...
}
catch(NumberFormatException e)
{
...
}
}
}
Notice the way that reading is performed. The condition in the while state-
ment is evaluated by first assigning a value to line (by invoking readLine).
The value of this assignment statement is the value assigned to line. We
then compare this value to null to determine whether or not the end of the
file had been reached. The loop is entered only if we have not yet reached
the end of the file.
Copying Files
If we do not want to interpret the contents of a file as strings, then we do
not need to use the wrapper classes. As a simple application along this
line, the next example shows how we can make a copy of a file.
Example 5
The following program uses FileReader and FileWriter to copy the con-
tents of a file named source.txt into a file called dest.txt. The program
reads characters from the reader as long as there is more input in the input
file and writes those characters to the writer. When the input runs out,
the program closes both the reader and the writer.
import java.io.*;
class Copy
{
public static void main(String[] args) throws IOException
{
FileReader in = new FileReader("source.txt");
FileWriter out = new FileWriter("dest.txt");
592 APPENDIX B. INPUT AND OUTPUT
int c;
while ((c = in.read()) != -1)
out.write(c);
in.close();
out.close();
}
}
Notice the way that the end of the input file is detected. The input stream
consists of bytes of data with integer values. The read method returns the
integer −1 when it detects the end of the input file.
Redirection
Often, programs that use files can make this easier with a simple but ef-
fective device called redirection. To use redirection, you must be able to
use a command line interface to run a program. If you are used to running
your Java programs in an integrated development environment (IDE), it
may be possible to do so from within the IDE but you may have to open
a command window. You should check the documentation of your IDE or
speak with your system administrator for the details.
As an example of redirection, suppose that we want to send all the
output from a program to the file out.txt. We can do so by first writing
the program as if all output was going to the standard output device (using
System.out.print and System.out.println). If we then compile the
program into a file called Sample.class, we can run the program using
the command
java Sample > out.txt
We can also use redirection for input, provided that all the input comes
from one file. To get input from the file in.txt, we write the program
as if input was coming from the standard input device. We then run the
program Sample using the command
java Sample < in.txt
Both features can be used at the same time so that the command
java Sample < in.txt > out.txt
would read all its input from in.txt and send all its output to out.txt.
Appendix C
Example 1
The number 4257 can be decomposed as follows.
4257 = 4000 + 200 + 50 + 7
= 4 × 1000 + 2 × 100 + 5 × 10 + 7
= 4 × 103 + 2 × 102 + 5 × 101 + 7 × 100
Example 2
The value (as a base 10 numeral) of the base 8 numeral 42578 can be
determined as follows.
42578 = 4 × 83 + 2 × 82 + 5 × 81 + 7 × 80
= 4 × 512 + 2 × 64 + 5 × 8 + 7 × 1
= 2048 + 128 + 40 + 7
= 2223
Example 3
If we want to use a base that is greater than ten, we must use more
than the normal ten digits. The usual solution to this problem is to use
letters of the alphabet to represent digits greater than 9. Thus
A (or a) is used to represent 10
B (or b) is used to represent 11
C (or c) is used to represent 12
and so on.
596 APPENDIX C. CHARACTERS AND INTEGERS
Example 4
The numeral AC3F16 is a valid hexadecimal (base 16) number. Its value
can be determined as follows:
Since A represents 10, C represents 12, and F represents 15, then
Example 5
(a) 10010110101111102 = 1001 0110 1011 11102 = 96BE16
(b) 6F7B016 = 0110 1111 0111 1011 00002
(c) 101001101110102 = 0010 1001 1011 10102 = 29BA16
Example 6
(a) The expression 0x3A2 represents the value 3A216
(b) The expression 057 represents the value 578
Example 7
The statement
System.out.println(2 + "\u00F7" + 3);
will print
2÷3
Example 8
The combination of the letters a and e is sometimes written as æ. The
Unicode representation of æ is 0000 0000 1110 0110 (\u00E6). Thus, the
statement
System.out.println("Write \"Caesar\" as \"C\u00E6sar\"");
will print
Write "Caesar" as "Cæsar"
The tables on the following pages show the graphic characters of Uni-
code’s Basic Latin and Latin-1 Supplement character sets. These are suf-
ficient for Western European languages. Central and Eastern European
599
HTML Basics
The idea of a markup language is an old one; it has been used by book
editors for many years. Before computers were used for typesetting, if a
book editor received a manuscript from an author, the editor would read
through it and insert marks to indicate, to the typesetter, the appearance of
the final printed document. Typically, marking up a text, an editor might
indicate such things as where paragraphs should start, whether something
should be centred on a line, whether a piece of text should be a section
heading, and so on. The typesetter would then use these directions to
actually compose the finished page. For example, if some text should be
typeset in italics, the editor would mark up the manuscript by underlining
the appropriate passage.
HTML works in a similar way. When we create a document using
HTML, we insert indicators of how we want the final product to look.
HTML does not specify the exact appearance of the final images. Instead
it specifies the general nature. For example, we can indicate that we want
a piece of text displayed as a major heading but we cannot control the font
or the exact size at which the text will be displayed. These details are
left for the browser to resolve so that different browsers and/or different
operating environments may produce slightly different displays.
HTML files consist of plain text and tags that tell the browser how
to interpret the text. These tags usually appear as pairs, indicating where
some feature is to begin and where it is to end. Such pairs are called
container tags.
Example 1
If we wanted some text written in italics, we could write
preceding stuff <i>italicized stuff</i> following stuff
The use of the tags <i> and </i> in Example 1 illustrates the general
form of container tag pairs; the opening of some part is indicated by a
start tag of the form <...> while the end of that part is indicated by an
605
end tag of the form </...>. Unlike Java, HTML is not case sensitive; we
could have indicated the bounds of the italicized passage by writing <I>
and </I> instead of <i> and </i>.
An HTML file begins and ends with the tags <html> and </html>.
Each file consists of two main sections: the head (enclosed by the tags
<head> and </head>) and the body (enclosed by the tags <body> and
</body>). The head can contain a title, delimited by the tags <title>
and </title>. The text within the title will appear in the title bar of the
window when the file is displayed in a browser. The body contains that
part of the file that should appear in the window when the file is displayed
in the browser. Comments that will not appear anywhere in the displayed
version of the file can be written by preceding the comment with <!-- and
following it with -->. (Some browsers allow you to eliminate the hyphens
but, to make sure that your comments will work everywhere, it is a good
idea to include them all the time.) The next example illustrates a complete
HTML file.
Example 2
The following code defines a complete HTML file. We have used indenta-
tion to make the structure of the file clearer but this is not necessary. With
HTML, as with Java, extra white space in a file is ignored.
<html>
<head>
<title> Italics </title>
</head>
<body>
<!-- Normal and italicized text -->
preceding stuff <i>italicized stuff</i> following stuff
</body>
</html>
Text Styles
As we have already noted, the exact form of the display of an HTML file
is under the control of the browser used to display the file. There are,
however, many ways in which we can guide the browser. The next table
shows some of the container tags that can be used with text along with the
results that we can expect when we use these tags.
Style Tag Display
Boldface <b> . . . </b> Boldface
Emphasis <em> . . . </em> Emphasis
Italic <i> . . . </i> Italic
Strike <s> . . . </s> Strike
Strong <strong> . . . </strong> Strong
Subscript <sub> . . . </sub> Subscript
Superscript <sup> . . . </sup> Superscript
Typewriter <tt> . . . </tt> Typewriter
Underline <u> . . . </u> Underline
Notice that the forms of the display of both Boldface and Strong are iden-
tical. The same is true of Italic and Emphasis. This is typical but not
necessarily always the case. A browser may have other ways of interpret-
ing Strong and Emphasis.
Since extra white space is ignored in HTML documents, simply leaving
one or more blank lines between passages will not cause those blank lines
607
• The <p> tag indicates the start of a new paragraph. This is similar
to a line break but it will leave a blank line before starting the new
paragraph. The end of a paragraph can be indicated by a </p> tag.
Although this is not necessary in HTML, it is required in XHTML,
the proposed successor to HTML.
• The <hr> tag places a horizontal rule across the page. It is useful for
separating sections of text.
Character Entity
space
< <
> >
& &
Example 3
The following HTML document uses all six possible header sizes.
<html>
<head>
<title> A Horde of Headers </title>
</head>
<body>
<h1> A Size 1 Header </h1>
<h2> A Size 2 Header </h2>
<h3> A Size 3 Header </h3>
<h4> A Size 4 Header </h4>
<h5> A Size 5 Header </h5>
<h6> A Size 6 Header </h6>
</body>
</html>
Here is a typical display of this file.
609
Lists
Example 4
The HTML file
<html>
<head>
<title> Unordered Lists </title>
</head>
<body>
Things to do today:
<ul>
<li> go for at least two walks
<li> chase squirrels
<li> kill cat next door
<li> nap between events
</ul>
</body>
</html>
610 APPENDIX D. HTML AND APPLETS
Example 5
The fragment
Things to do today:
<ol>
<li> go for at least two walks
<li> chase squirrels
<li> kill cat next door
<li> nap between events
</ol>
611
would be displayed as
A third type of list structure, called a definition list, is useful for any
lists in which items have two parts. Here the list is contained by the tags
<dl> and </dl> while the parts of each item in the list are preceded by
<dt> (for definition t erm — the term to be defined) and <dd> (for definition
description — the description of the term).
Example 6
The definition list
<dl>
<dt> Excellent Things
<dd> pizza, ice cream, tummy rubbing, car rides,
my friend Sampson
will be displayed as
Example 7
To display the file called sample.gif, we could write
<img src = "sample.gif">
The code src = "sample.gif" is an example of an attribute. The left side,
src, is the name of the attribute while the right side, "sample.gif" is its
value.
Example 8
Suppose that an HTML document contains the following code.
<img src = "allan.gif" alt = "My friend Allan">
If a browser cannot display the file allan.gif, it will substitute the text
My friend Allan at the appropriate location.
Example 9
To produce a document with purple text on a yellow background, we can
add attributes to the opening body tag as follows.
<body text = #9900FF bgcolor = #FFFF00>
It is also possible to change the colour of individual words or even individual
characters within a document but these topics are beyond our scope.
Links
The feature that distinguishes hypertext from ordinary text is the fact
that, by clicking on an area of hypertext, we can jump from viewing one
document to viewing another that may be anywhere else in the world. To
allow us to do so, HTML has anchor tags (that provide anchors to links).
Anchors are container tags whose basic form is
<a href = "pathToDocument">
displayedText
</a>
When displayed, code of this form would show displayedText, in colour
and underlined. Clicking on that text with a mouse would cause the file
pathToDocument to be displayed in the browser. The name href stands
for hypertext ref erence. The pathToDocument can represent a path to:
• a local file
Example 10
Suppose that our current directory has a subdirectory called lower, that
directory has a subdirectory called evenLower, and we want to create a
link to the file deepFile in that directory. An anchor for this file could
have the form
<a href = "lower/evenLower/deepFile">
Other File
</a>
This anchor would display “Other File” and, if this were clicked on, the
browser would display the file deepFile. It is also possible to link to files
that are on the same machine but not in a subdirectory of the current
directory but that is a bit too complex for us to handle.
Example 11
Suppose we have an HTML file dealing with a computer science course. At
some point in the file, we have a heading:
<h2> Test Solutions </h2>
At another point in the file, we want to have the file display the text
616 APPENDIX D. HTML AND APPLETS
Applets
Now that you know something about creating HTML files, you are ready
to learn how to create applets — Java programs that can be embedded
in HTML documents. When we say that an applet is “embedded” in an
HTML document, we mean that, when the HTML file is displayed, the
applet, like an image or a piece of text, will occupy part of the display area
of the file. Within that area an applet can, like an application program
operating in a GUI, display results, obtain input via buttons or text fields,
and so on.
To include an applet in an HTML document, we use an <applet>
container tag pair. These tags can contain a number of attributes but we
will examine only three.
• width — the width, in pixels, that the applet will occupy on the
screen
Example 12
If we had an applet that had been compiled into a byte code file called
Sample.class, we could include the applet in an HTML file by writing
617
<applet
code = "Sample.class"
width = "300"
height = "200">
</applet>
As usual with HTML code, the spacing that we have shown is not neces-
sary; it would be perfectly acceptable to scrunch everything onto one line
as follows:
<applet code="Sample.class" width="300" height="200"></applet>
Container
6
Panel
6
Applet
6
JApplet
The JApplet class inherits a number of methods from the Applet class.
Four of these methods are used by browsers to control the execution of
applets. They are:
• public void init ()
This method is called once by the browser, when the applet is first
loaded. It is used to perform initialization tasks.
• public void stop ()
During the course of its lifetime, an applet may not always be visible.
618 APPENDIX D. HTML AND APPLETS
Any time that the browser leaves the page containing the applet, the
stop method is called.
In addition, applets inherit, from the Container class, the method paint
with header
public void paint (Graphics g)
This is called automatically every time that the applet needs to be re-
painted. For example, if a user covers an applet with another window and
then removes that window, paint is called automatically to repaint the
applet.
So that you can see the differences between applications and applets,
our first example will be very close to the one shown in Example 4 of
Section 13.1 — another greeting to the world.
Example 13
The following applet, when displayed, will show the message “Hello, world”.
import javax.swing.*;
import java.awt.*;
<html>
<head>
<title>Hello Applet</title>
</head>
<body>
Here it is:
<applet
code = "AppletGreet"
width = "400"
height = "100">
</applet>
our first applet!
</body>
</html>
If we load this file into a browser, it will produce a display like the following.
Notice that space has been allocated to the applet in the same way that it
was done with a graphic file. The applet appears between the two blocks
of text with the bottom of the applet’s region aligned with the baseline of
the surrounding text.
following.
The appletviewer program can be very useful when developing and testing
an applet. Most Java development tools give easy access to appletviewer.
Check with your system administrator for details.
As a final variation on our greetings, the next applet displays the
familiar message but it uses the start method to produce variations.
Example 14
The applet that follows displays the message “Hello, world” in a font whose
size is chosen randomly between ten points and fifty points. Initially, the
font size is set to 30 points, for use by the init method. Subsequently,
the start method assigns a random value (between 10 and 50) to this
field. If you run the applet in appletviewer, you can see the effect of the
randomization by repeatedly minimizing the applet and then restoring it.
Each time that you do so, the start method will be called and another
font size will be chosen.
import javax.swing.*;
import java.awt.*;
Exception Handling
Both errors and exceptions are objects that are created (or thrown) when
unusual situations occur. The root class of all such objects is java.lang.-
Throwable. It has two subclasses: Error and Exception.
Objects of the Error class are thrown when very bad things happen.
These include recursion to a depth that causes a stack overflow or the JVM
running out of memory (with the garbage collector unable to allocate more
memory). Such problems cause termination of the program.
Exceptions, on the other hand, are thrown when less catastrophic sit-
uations arise. As examples, exceptions are thrown if an array index is out
of bounds, an attempt is made to examine a field of a null object, or an
attempt is made to read from a file that does not exist.
Exceptions are further subdivided into two major groups: unchecked
exceptions are those that the programmer need not handle in any way while
checked exceptions require that the programmer write some instructions to
take care of them.
Unchecked exceptions (for which no action need be taken) belong to the
class RuntimeException or one of its subclasses. These are, generally, ex-
ceptions that can be thrown in a very wide variety of contexts so that having
to handle them would tend to clutter up programs. Unchecked exceptions
that we have seen in this text include an attempt to perform integer divi-
sion by zero (throwing an ArithmeticException), an attempt to use an
incorrect string index (throwing a StringIndexOutOfBoundsException),
an attempt to use an incorrect array index (throwing an ArrayIndexOut-
OfBoundsException), a numerical value in an incorrect format (throwing
a NumberFormatException), or an attempt to examine a field or invoke a
method of a null object (throwing a NullPointerException).
Checked exceptions, requiring some action on the part of the program-
mer, are usually thrown as a result of some problem from which a graceful
exit may be possible. Java offers a number of ways of handling such excep-
tions. The simplest thing that we can do is to pass the problem on. This
is done through the use of a throws clause in the header of the method in
which the exception may occur. A throws clause takes the form
throws <exception list>
where <exception list> is a sequence of one or more exception class identi-
fiers, separated by commas.
Example 1
Suppose that the method getData contains code that might generate either
an EOFException or a FileNotFoundException, both of which are checked
exceptions (in the package java.io). The header of the method might take
625
the form
public void getData () throws EOFException,
FileNotFoundException
Example 2
A catch clause that could be used to catch an EOFException might have
the form
catch (EOFException e)
{
...
}
Example 3
A catch clause of the form
catch (Exception e)
{
...
}
will be executed for any type of exception because Exception is the super-
class of all exceptions.
If there is more than one catch clause, Java will attempt to match
them in the order that they are written. Consequently, they should be
arranged so that more general ones follow more specific ones.
Example 4
The class FileNotFoundException is a subclass of IOException which
itself is a subclass of Exception. If we were to have catch clauses for each
of these classes, they should be arranged in the order
catch (FileNotFoundException e)
{...}
catch (IOException e)
{...}
catch (Exception e)
{...}
If we were to arrange them in the reverse order, only the first catch clause
would ever be executed because Exception provides a match for any ex-
ception.
627
Example 5
The try/catch fragment shown here could be used to double the size of
an int array that is not large enough to store a sequence of elements.
try
{
list[n] = next;
}
catch (ArrayIndexOutOfBoundsException e)
{
int[] tempList = new int[2*list.length];
for (int i = 0; i < list.length; i++)
tempList[i] = list[i];
list = tempList;
list[n] = next;
}
finally
{
n++;
}
628 APPENDIX E. EXCEPTION HANDLING
Since a finally clause is always executed after any try/catch blocks, this
would ensure that n is incremented after both a normal insertion and an
exceptional insertion.
Of course, we could have simply checked the size of the array before
attempting to store a new element in it and increased the size of the array on
the spot. It can be argued, however, that using a catch block encapsulates
the exception handling and makes the rest of the code easier to follow. (It
could also be argued that doing so would have ruined our example.)
If we don’t know how to fix a problem, we can either let the exception
be thrown right up to the JVM which will write a message and terminate
execution or we can catch the problem and write a message ourselves.
Example 6
The following fragment will catch a FileNotFoundException and then halt
the program.
catch (FileNotFoundException e)
{
System.out.println(e);
System.exit(1);
}
The exception object, e, has a toString method that returns the name of
the class. The exit method terminates execution. The argument value,
one, signals an abnormal termination. (Normal termination is indicated by
an argument of zero.)
Example 7
If we want to consider an empty string ("") to be an error, then we can
catch this error by writing
if (s.equals(""))
throw new RuntimeException("Empty string encountered");
629
If s has the value "", then the program will print the message contained
in the constructor and terminate execution. By throwing a RuntimeEx-
ception, we need not put the throw statement inside a try block because
exceptions of this class are not checked.
Introduction
JavadocTM is a tool that is included with the Java software development
kits (SDK’s) from Sun. It can be used to generate documentation in HTML
format from special comments in the source code of a Java program. These
special comments, called doc comments, can have two parts: a description
followed, possibly, by a number of tags.
A doc comment starts with the characters /**. These characters
should be alone on a line, aligned vertically with the code that follows.
Following lines normally start with an asterisk followed by a blank, with
the asterisk aligned with the first asterisk on the first line. The last line
of a doc comment contains the sequence */ to end the comment. The as-
terisk here should be aligned with those in the preceding lines. The tag
section, if it exists, should be separated from the description section by a
line containing only an asterisk.
Example 1
The structure of a doc comment is shown here.
/**
* This is an example of a doc comment.
* This is the description section.
*
* This is the (optional) tag section.
*/
Code that follows the comment should be aligned like this.
• @author
The comment should state the author or, if the author is unknown
or shy, should be “unascribed”. If there is more than one author,
each one can be listed with a separate tag. The @author tag is used
only for classes and interfaces. The comment line
* @author Lori Towstiak
will generate HTML code that will be displayed as
Author:
Lori Towstiak
• @version
The comment should state the version number and date. This tag,
too, is used only for classes and interfaces. The line
* @version 1.2 2002-02-12
will generate HTML code that will be displayed as
Version:
1.2 2002-02-12
• @param
This tag is used only with methods and constructors. One tag and
comment is required for each parameter. The first word of each com-
ment should be the identifier of the parameter. The rest of the com-
ment is a description of the parameter. By convention, the first noun
in the description is the data type of the parameter. (Articles like
“a”, “an”, or “the” can precede the noun.) Every parameter must
have its own @param tag; they cannot be grouped or omitted.
Example 2
The entry
* @param surname a <tt>String</tt> containing a
* student’s family name
* @param initial a <tt>char</tt> containing the
* initial of the first given name
will generate HTML code that will be displayed as follows in a pa-
rameter list for the constructor or method that is being documented.
Parameters:
surname - a String containing a student’s family name
initial - a char containing initial of first given name
635
• @return
This tag is used only with methods that return a value. The com-
ment should state both the type of the value returned and the range
of possible values that it can take (if the range is restricted). The
description should also note return values used for special cases (such
as a search returning −1 if a value is not found). The @return tag is
required for every method that returns something other than void.
• @throws
This must be used to specify any exceptions that a method might
throw using a throws clause in its header. It can also be used for
unchecked exceptions that can be thrown by the method and the
programmer might want to catch. The first part of the comment is
the class name and the second part is a description of possible causes
of the exception. The tag @exception is a synonym for @throws.
• @see
This tag can be used to create cross references, including hyperlinks,
to other items. All the @see tags will be used to create HTML text
that will display as a list with the header “See Also:”.
The simplest form that these tags can take is
@see "<a string>"
This is useful for making a reference to a book or some other docu-
ment.
Example 3
Writing
* @see "The Art of Computer Programming, Vol. 1"
in a doc comment produces HTML code that will display as
See Also:
“The Art of Computer Programming, Vol. 1”
The @see tag can also take a form that creates a reference to the
documentation of some other feature of the Java language. This
form is
@see <package>.<class>#<member> <label>
636 APPENDIX F. THE JAVADOC PROGRAM
This will create HTML code that will display <label> in the “See
Also:” list as a hypertext link to <package>.<class>.<member>.
If <member> is in the current class, then both the <package> and the
<class> can be omitted. If <member> is in the current package, then
<package> can be omitted. If <member> is a method or constructor,
then it should contain a parameter list (or a list of parameter types)
separated by commas and enclosed in parentheses. If <label> is
omitted, then the HTML code will display the name of the member.
In this case, if the member is in the current package, then Javadoc
will only display <class>.<member>; if the member is in the current
class, Javadoc will only display <member>.
Example 4
Assuming that List is a class in the current package, foo is a method
in the current class, and age is a field in the current class, then the
comments
* @see java.lang.String#toUpperCase()
* @see #foo(int i, char c)
* @see List#add(double)
* @see #age Age in years
will generate code that will display as
See Also:
java.lang.String.toUpperCase()
foo(int i, char c)
add(double)
Age in years
• @link
This tag operates slightly differently from the others. Rather than
being written on a line by itself, this tag can be used within other
parts of a doc comment to provide in-line hyperlink references. The
form of such a tag is
{@link <package>.<class>#<member> <label>}
The form used is similar to that for @see but, instead of creating an
item in a list (as @see does), this simply creates an in-line reference.
637
Example 5
A doc comment containing the text
. . . uses the {@link #getNext()} method to . . .
would, assuming that the getNext method is in the same class as the
item being documented, generate HTML code that would display as
. . . uses the getNext() method to . . .
Double-clicking on the underlined text would then produce a jump
to the documentation of the getNext method.
are to be used, they should be written on the command line, just after
the javadoc command, separated by spaces. Some of the more common
options are shown here.
-author
Include in the documentation the text specified by the @author tag.
Without this option, the information specified by any @author tags
will not be printed.
-d
Specify the destination directory of the HTML files generated by
javadoc. The name of the directory should follow the option, sepa-
rated by a blank. If this option is omitted, the files will be saved in
the current directory.
-doctitle
Specify a title to be written on the overview summary file produced
by javadoc. The title should follow the option, separated by a blank.
-package
Show documentation only for public, protected, and package classes
and members. Omit documentation for private classes and members.
-private
Show documentation for all classes and members (public, protected,
package, and private).
-protected
Show documentation only for public and protected classes and mem-
bers. Omit documentation for package and private classes and mem-
bers.
-public
Show documentation only for public classes and members. Omit doc-
umentation for protected, package, and private classes and members.
-version
Include in the documentation the text specified by the @version tag.
Without this option, the information specified by the @version tag
will not be printed.
Appendix G
Java Operators
The title of this book was chosen to to give the idea that
we use Java to teach about computer science; the book
is not intended to be a complete description of Java. In
particular, although most operations are covered (pri-
marily in Chapters 2 and 3), some have been omitted.
In this appendix, we give a summary of all of Java’s
operators along with an explanation of the function of
those operators that are not discussed elsewhere in the
text.
640 APPENDIX G. JAVA OPERATORS
Boolean Operators
In Chapter 3, we discussed the boolean operators &&, ||, and ! whose
operands have the values true or false. In addition to these, Java has
three other rarely seen boolean operators: &, |, and ^.
Bitwise Operators
Bitwise operators manipulate the individual bits of values of type byte,
short, int, long, or char in a variety of ways. Bitwise operations are
similar to operations with boolean-valued operands. Here, however, a bit
value of 1 is equivalent to a boolean value of true and a bit value of 0 is
equivalent to a boolean value of false. In executing bitwise operations,
Java compares the bits in corresponding positions in each of the operands.
(If either operand is of type long, the result is long. Otherwise, the values
of the operands are promoted, if necessary, to type int and the result is
of type int.) The actions of the bitwise operators & (and), | (inclusive
or), ^ (exclusive or), and ~ (complement) are summarized in the following
table.
p q p&q p|q p^q ~p
1 1 1 1 0 0
1 0 0 1 1 0
0 1 0 1 1 1
0 0 0 0 0 1
Example 1
Suppose that we have made the following declarations:
byte a = 12, b = 10;
The resulting 8-bit binary representations of a and b are:
a = 000011002 and b = 000010102
(d) ˜a
The statement
f = ~a;
will assign to f the value −13 because
~00001100
⇒ 11111111 11111111 11111111 11110011
and this bit pattern represents the value −13.
Notice that ~a 6= −a. They are, however, related: for any value of x,
it is always true that ~x = −x − 1.
Shift Operators
Java has three operators that allow us to shift the positions of the bits
in an integer variable, either to the left or to the right. The shift operators
are: left shift <<, signed right shift >>, and unsigned right shift >>>. The
left-hand operand of a shift operator is the value to be shifted while the
right-hand operand specifies the shift distance.
Example 2
The statement
int i = 9 << 3;
will assign to n the value 72. To see why this is so, note that 9 = 10012.
643
After shifting three places to the left, the result has value 10010002 =
64+8 = 72. Note also that 72 = 9×23 . Shifting three places has multiplied
the value by 23 .
Example 3
Suppose that a and b are int variables with the following bit patterns:
a: 00000000 00000000 00000000 00110101
b: 10000000 00000000 00000110 10001011
Then
a >> 2 gives 00000000 00000000 00000000 00001101
b >> 8 gives 11111111 10000000 00000000 00000110
b >>> 8 gives 00000000 10000000 00000000 00000110
Example 4
The following fragment will print the bit representation of an int variable
n. It uses a mask that contains a single bit set to 1 and all others set to 0.
If the 1 bit is i bits from the left end of the mask, then, when the mask is
644 APPENDIX G. JAVA OPERATORS
anded with n, the result will be zero if and only if that bit is zero in the
representation of n. By moving the bit in the mask through all 32 possible
positions in an int value, we can determine the value of every bit in n.
// Initialize mask to 10000000 00000000 00000000 00000000
int mask = 1 << 31;
String result = "";
for (int i = 0; i < 32; i++, mask >>>= 1)
if ((mask & n) == 0)
result += "0";
else
result += "1";
System.out.println(n + " has representation: " + result);
Conditional Expressions
Although most expresssions in Java are either unary (one operand and one
operator) or binary (two operands and one operator), Java also has one
ternary expression (three operands and two operators). The expression is
called a conditional expression and it has the following form:
<expression1 > ? <expression2 > : <expression3 >
To evaluate the entire expression, <expression1 > (which must be a boolean-
valued expression) is evaluated first. If its value is true, then the value of
the entire expresson is the value of <expression2 >; if, however, the value
of <expression1 > is false, the value of the entire expression is the value
of <expression3 >.
Example 5
If the value of n is 2, then the value of the expression
n > 0 ? "correct" : "wrong"
is "correct" because the value of n > 0 is true.
Example 6
To assign to the variable larger the larger of either a or b, we could write
if (a > b)
larger = a;
else
larger = b;
Using a conditional expression, the same effect can be achieved with less
work by writing
larger = a > b ? a : b;
Example 7
The signum function has the following definition:
−1 if x < 0
signum(x) = 0 if x = 0
1 if x > 0
Example 8
Example 9
(a) The expression 12 / 2 * 3 contains two arithmetic operators with
equal precedence. Since these operators are evaluated from left to
right, the evaluation of the expression proceeds as follows:
12 / 2 * 3
⇒ 6 * 3
⇒ 18
(b) The expression i = j = k = 0 contains three assignment operators.
Since assignment operators are evaluated from right to left, the eval-
uation of the expression proceeds as follows:
i = j = k = 0
⇒ i = j = 0
⇒ i = 0
⇒ 0
Appendix H
Answers to Exercises
a b 1.12
1. Here we are (finally),
at the end
- "two" - "one" of
(d) Chapter 1
3. (a) int
a b (b) double
(c) char
(d) Illegal – decimal point
(e) char
- "zwei" - "drei" (f) String
(g) String
"ein" (h) float
(i) Illegal – should be ’\’’
2. c is dog (j) Illegal – need double quotes
d is cat (k) long
(l) Illegal – comma
1.9 4. (a) short
1. (a) The value of i is -47 (b) int
652 APPENDIX H. ANSWERS TO EXERCISES
4.6 4
1. ***** 2
**** 1
*** 2. (a) Sum of the integers from 0 to 10
** (b) Number of factors of 2 in n
* (c) Sum of the digits of n
7. (b) Guess middle of each interval
4.7
1. An error message; 5.1
i is not defined outside the loop 1. Definition – states what method does
2. (a) Any value will stop it Invocation – causes it to be executed
(b) Nothing will stop it 2. 4 5 1 2 3 6 7 1 2 3
3. (a) Cannot escape the loop
(b) Change || to && 5.2
4. It is an infinite loop; 1. i = 7 and j = 3
it has an empty body method swaps copies of i and j
2. (a) Invalid – cannot pass 0.5 to int
4.8 (b) Valid
1. (a) 0 (c) Invalid – cannot pass 2.0 to int
1 (d) Valid
4 (e) Valid
. (f) Invalid – cannot pass 3L to int
25
(b) 4 5.3
5 1. (a) mystery
. (b) a b
8 (c) int
(c) 25 (d) The first line
21 2. (a) Returns lesser of its parameters
17 (b) Returns negative difference of
. its parameters
1 3. (a) Valid
(d) 1 (b) Valid
2 (c) Invalid – println returns no
6 value
24 (d) Valid but silly since f(x) is lost
(e) 13
40 5.4
20 1. Answers may vary
10 Math.abs is one possibility
5 2. (a) Valid – B – exact match
16 (b) Invalid – cannot pass 2L to int
8 (c) Valid – B – only need widen ’A’
659
5.6 6.1
1. 1: i x 1. Variable is declared within a method;
2: i x j field is declared outside a method.
3: i x j k 3. No new Fraction object was created.
4: i x j k y 4.
5: i x j p
6: i x j f
7: i x j z
2. Code enclosed by {...} Fraction
3. public: can be accessed anywhere
private: only within that class - num 1
4. main den 3
5.8 q r
1. (a) No return if a = b
(b) No effect at point of call
(c) Value of parameter is ignored Fraction
5.9 - num 4
1. (a) Those that return a value: den 3
type returned is in header
Those that return no value:
have void in header
(b) Those that return a value: 6.2
4
call is in context appropriate 1. (a) 5
7
for the type of value returned (b) −5
5
Those that return no value: (c) 6
−9
call is alone in a statement (d) −12
13
2.Argument: an expression whose value (e) 20
is passed to a method
Parameter: variable in a method
header 6.4
assigned value of argument 1. Accessor methods can inspect fields;
3. The entire method mutator methods alter them.
4. 1: a b 2. valid – private fields are
2: a b c visible within their class
3: a b c d
4: a b c d e
660 APPENDIX H. ANSWERS TO EXERCISES
c3
6.5
1. p and q refer to the same object
Circle
2. (a)
x 2
p - y 3
r 1
Fraction
(d) true
- num 2
(e) false
den 3
q 6.6
1. (a) A is the class method;
only A’s header contains static
Fraction
- num 1
den 6 6.7
1. There is only one copy of a class field;
each object has its own copy of each
(b) instance field.
2. Use the final modifier.
3. Constants use upper case letters.
q p
4. (a) <class identifier>.<field identifier>
(b) <object identifier>.<field identifier>
6. (a) Class: same for all accounts
Fraction (b) Instance: each account unique
- num 1 (c) Instance: each account unique
den (d) Class: same for all accounts
6
3. (c)
6.8
1. == is only true if references
c1 c2
point to the same location
2. Not always; if the common divisor is
Circle
4 = 2 × 2, only one 2 would be found
x 2 2000
5.
- 2001
y 3
r 1
661
6.9 Animal
1. If no constructor is defined (d)
6
2. Whenever == returns true
3. (a) The implicit Fraction object Vertebrate Invertebrate
(b) this.reduce();
(c) Delete references to this 6 6
Mammal Insect
6.10 6
1. A class defines the fields and
methods that an object can Human Dog
contain.
2. A class field contains static; 6
an instance field does not. Samoyed Husky Beagle
3. A call to an instance method
contains the form: (e) Answers may vary – possibly:
<object id>.<method id> Fish or Bird
5. 2. Answers may vary – possibily:
(a) Vegetable
z1 z3 (b) House
(c) Woodwind
(d) Particle
Complex 3. No, Object has none
4. “inherits” means above in a class
- re 2
hierarchy
im 1 “extends” means immediately above
5. (a)
z2 Shape
6
Complex Polygon
- re 1 6
im −4 Quadrilateral Triangle
6
7.1 Rectangle
1. (a) Vertebrate
6
(b) Mammal
(c) Animal,Vertebrate,Mammal,Dog Square
662 APPENDIX H. ANSWERS TO EXERCISES
Object
7.3
1. (a) valid
(b) invalid, unfixable
(c) valid
Shape
(d) needs a cast to Student
(e) valid
(f) valid
(g) valid
Polygon
2. A part
A part
B part
A’s m
Quadrilateral
B’s m
B’s m
7.4
7.2 1. This is a call to a constructor of the
superclass (with argument "man")
1. (a) valid
2. (a) A
Car has a Vehicle part
(b) valid (b) A
B
Same class
(c) This would cause an error
(c) invalid
MotorVehicle has no Bus part during compilation
(d) valid 7.5
Truck has an Object part 1. Its class must also be abstract
(e) invalid 2. A1
Bus has no Car part B1
(f) invalid B2
Vehicle has no Bus part C2
(g) valid 3. (a) a2 is of type A which has
LightTruck has a no m2 method
MotorVehicle part (b) ((C)a2).m2;
2. (a) yes (c) In A: make class abstract
(b) no and declare an abstract m2
(c) no (d) new A() would fail; cannot
(d) yes instantiate an abstract class
3. (a) invalid, unfixable
(b) valid 7.6
(c) requires a cast to Samoyed 1. (a) 6
(d) requires a cast to Dog 4.0
(e) valid (b) overloading
663
(e) $2 (h) 14
(f) 86.00
10.3
9.6 1. 2 6 8 3 1 7 4
1. one three two 2683174
2. (a) ’s’ 2368174
(b) 5 1236874
(c) "Titus" 1236784
(d) 12 1234678
(e) "y" 3. If an item had to be inserted at the
(f) ’y’ front, the program would crash
(g) −1 6. Yes – an item is only moved in front
(h) 11 of another if it is smaller
(i) "alyssa titus"
(j) "lyssa " 10.4
(k) false 1. Renée Brien Scarlett Doris Vincent
(l) true Renée Brien Doris Scarlett Vincent
3. (a) true Doris Brien Renée Scarlett Vincent
(b) false Brien Doris Renée Scarlett Vincent
(c) true 2. The list would be in decreasing order
(d) false 3. (a) Place a test in the code to swap
(e) true only if top != largeLoc
(f) true (b) The test would probably waste
more time than it saved since
10.1 values would usually be unequal
1. Index of item’s last occurrence 4. (a) 1 9 6 8 2 4
3. (c) Frequently sought items would 126894
migrate to the front of the list 124896
124698
10.2 124689
1. In each part the search would exam-
ine 10.5
(a) 55 and 72 1. 3:8 3 2 7 5
(b) 55, 34, 49, and 41 3 8:3 2 7 5
(c) 55, 72, 60, and 67 3 3 8:2 7 5
4. 3 3 3 2 8:7 5
5. (a) 2 3 3 2 7 8:5
(b) 4 332758
(c) 5
(d) 6 3:3 2 7 5 8
(e) 7 3 3:2 7 5 8
(f) 9 3 2 3:7 5 8
(g) 10 3 2 3 7:5 8
666 APPENDIX H. ANSWERS TO EXERCISES
323578 (d) c + 6
4. (a) 600 s
3:2 3 5 7 8 (b) About 120 000
2 3:3 5 7 8 5. (a) About 1.3 s
2 3 3:5 7 8 (b) About 80 000
233578 6. Insertion sort: about 3 h
Shellsort: about 2 min
2:3 3 5 7 8
2 3:3 5 7 8 10.9
233578 1. 0 12 Fox
7 12 Owl
3. 2:9 4 6 1 7 7 8 Gnu
2 9:4 6 1 7 8 8 Hen
2 4 9:6 1 7 4. (a) 5 1 4 8 3 6 9
2 4 6 9:1 7 5146389
2 4 6 1 9:7 (b) 1 5 4 8 9 6 3
246179 1458963
(c) 1 4 5 8 6 3 9
2 4 6 1:7 9 1456389
2 4 6:1 7 9 5. 53 13 14 50 12 70 29
2 4:1 6 7 9 26 61 54 86 75 70 65
2:1 4 6 7 9 7. (a) Answers may vary
124679
11.1
10.6 1. (a) You are at the top
1. 19 18 21 22 29 26 37 (b) There are more steps to climb
26 41 63 47 61 72 55 2. A process will not stop without it
2. 26 19 21 18 26 22 47 3. No; the song never ends
41 55 29 61 72 63 37 4. (a) and (b)
3. 121, 364, 1093 5. while (not at the top)
4. Last insertion sort will be very take one step;
fast since data are almost sorted 6. Answers may vary
7. Answers may vary
10.7 8. Ted, Grace, Michael, David,
1. (a) 4t s Liliane, Kirk, Raymond,
(b) 9t s Anne, Paul, Eura, Gerry
(c) 25t s
(d) 100t s 11.2
2. (a) 8t s 1. (a) 3, 5, 7, 9, 11
(b) 64t s (b) 2, 0.5, −1, 2, 0.5
3. (a) c+1 (c) 2, 3, 5, 8, 12
(b) c+2 (d) 1, 3, 4, 7, 11
(c) c+3 (e) 2, 1.41, 1.55, 1.60, 1.61
667
11.10 12.3
1. Answers may vary 1. Answers may vary
2. Answers may vary 4. Insertion, yes; deletion, no
3. (a) f (x, y) calculates x × y 5. (a) 123, 132, 213, 231, 321
for non-negative x, y (b) 1234, 1243, 1324, 1342, 1432,
4. (b) 9 2134, 2143, 2314, 2341, 2431,
(d) Non-recursive version should be; 3214, 3241, 3421, 4321
it can avoid repetitive calls
6. (a) if n >= 1
print a row of n asterisks
print an (n-1) pattern 12.4
print a row of n asterisks 1. Reverse the order of printing
7. (a) T2 (x) = x2 − 1 2. If head is null, an exception is
T3 (x) = x3 − 2x thrown by: item < head.info
(b) 5 3.To INSERT IN TAIL OF A NON-EMPTY LIST
8. (a) i) ((x2 )2 × x)2 if tail empty or item precedes tail
ii) ((((x2 ) × x)2 )2 )2 × x insert as second node of list
iii) ((((((x2) × x)2 )2 )2 × x)2 )2 else INSERT IN TAIL OF NON-EMPTY TAIL
(b) 4, 6, and 8
12.1
1. B: ref.link 12.5
C: ref.link.link 1. (a) A
D: ref.link.link.info (b) H, E, I, J, and K
(c) B, D, E, and H
12.2 (d) F and G
1. (e) B-C or D-E or F-G or J-K
(f) F and I
Node
temp (g) A
current - info 30 item (h) D
link 28 2. (a) 15 52 28 32 64 20 26 39 35
(b) 15 28 32 52 20 26 35 39 64
6
@
R
@ ?
(c) 64 52 15 32 28 39 26 20 35
Node Node Node 3. (a) 22
info 24 info 28 info 35 (b) i. + ÷ 8 2 − × 3 8 6
- - ii. 8 ÷ 2 + 3 × 8 − 6
link link link -
iii. 8 2 ÷ 3 8 × 6 − +
670 APPENDIX H. ANSWERS TO EXERCISES
(c)
Z
× 12.7
ZZ
1. It fails if the list is empty
+ +
J J
J J 12.8
1. It reverses a list
2 3 5 −
2. (a) A B D E H I C F G
J (b) D B H E I A F C G
J
4 2 (c) D H I E B F G C A
J
J
12.6
3 1
J
1. Replace “. . . all less than . . . ” J
by “. . . all less than or equal to . . . ”
2. (a) Humie, Philip, Mary, Michael 2 2
(b) Humie, Angela, Dan, Betsy
(c) Humie, Philip, Mary, Michael
(d) Humie, Angela, Dan, Dennis 1 3
J
3. (a) left child of Mary
J
(b) left child of Thomas
4. 2 2
J
Mercury J
HH
3 1
H
Earth Venus 4.
AA
Mars Saturn Grumpy
H
AA HH
Jupiter Neptune Uranus Doc Happy
AA AA AA
Pluto Bashful Dopey Sneezy
5. (a) It would be inserted in right
Sleepy
subtree of item already in tree
671
5. B: p.lChild TriColourFrame
C: p.lChild.info 2. Should use: "red" instead of ’red’,
D: p.lChild.rChild () after getActionCommand, equals
E: p.rChild.info method instead of == operator
13.1 13.5
3. Nothing would appear in the window 1. (a) A click is a press followed
all text is above the baseline at (0, 0) quickly by a release
4. (a) Change PLAIN to ITALIC (b) A MouseListener requires five
(b) Change PLAIN to methods: for presses, clicks,
BOLD+Font.ITALIC releases, enters, exits;
A MouseMotionListener
13.2 requires two: for mouse motions
1. (a) (0, 0) while pressed or not
(b) (150, 200) (c) First is an interface;
(c) (75, 100) second implements interface
(d) (225, 150) 2. Only one method to be implemented;
adapter would be pointless
13.3
1. (a) 3 rows, 5 columns 13.6
(b) 5 rows, 3 columns 1. Add: result.setEditable(false);
(c) 4 rows, 4 columns
(d) 3 rows, 6 columns 13.7
1. (a) Use red, not Red
13.4 (b) Should use a font object giving
1. Replace ColourFrame by font name, style, and size
Index
672
INDEX 673
unary plus/minus, 54
underflow error, 83
Unicode, 20, 69, 93, 370, 594–601
Uniform Resource Locator, 615
URL, 615
valueOf, 358
variable, 22
declaration, 24
initialization, 27
scope, 189
shadowing, 261
visibility, 257
package, 236
void, 7