John Carter Java

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 697

An Introduction

to
Computer Science
Using Java

John Carter
An Introduction to Computer Science Using Java

Copyright c 2003 John Carter

All rights reserved. No part of this publication may be reproduced, stored in a


data base or retrieval system, or transmitted, in any form or by any means,
mechanical, electronic, photocopying, recording, or otherwise, without the prior
written permission of John Carter.

National Library of Canada Cataloguing in Publication Data


Carter, John, 1940-
An introduction to computer science using Java / John Carter.
Includes index.
ISBN 0-9733427-0-6
1. Java (Computer program language) 2. Computer science. I. Title.
QA76.73.J38C375 2003 005.130 3 C2003-903559-X

Java, JDK, and Javadoc are trademarks of Sun Microsystems, Inc.


Windows is a registered trademark of Microsoft Corporation.

Printed and bound in Canada by University of Toronto Press

Corrections to known errors can be found at


http://www.ecf.utoronto.ca/∼jcarter/text/errata.html

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

2 Programs That Calculate 53


2.1 Basic Arithmetic Operations . . . . . . . . . . . . . . . . . 54
2.2 Assigning and Printing Expressions . . . . . . . . . . . . . . 59
2.3 Increment and Decrement Operators . . . . . . . . . . . . . 62
2.4 More Assignment Operators . . . . . . . . . . . . . . . . . . 65
2.5 Arithmetic and Characters . . . . . . . . . . . . . . . . . . 69
2.6 Using Math Methods . . . . . . . . . . . . . . . . . . . . . . 72
2.7 Avoiding Errors and Debugging . . . . . . . . . . . . . . . . 82
2.8 Review Exercises 2 . . . . . . . . . . . . . . . . . . . . . . . 86

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

6 Classes and Objects 213


6.1 Creating Objects . . . . . . . . . . . . . . . . . . . . . . . . 214
6.2 Instance Methods . . . . . . . . . . . . . . . . . . . . . . . . 221
6.3 Constructors . . . . . . . . . . . . . . . . . . . . . . . . . . 230
6.4 Hiding Information . . . . . . . . . . . . . . . . . . . . . . . 236
6.5 Comparing and Displaying Objects . . . . . . . . . . . . . . 240
6.6 Class Methods . . . . . . . . . . . . . . . . . . . . . . . . . 246
6.7 Class Fields . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
6.8 Putting the Pieces Together . . . . . . . . . . . . . . . . . . 251

vi
6.9 Avoiding Errors and Debugging . . . . . . . . . . . . . . . . 259
6.10 Review Exercises 6 . . . . . . . . . . . . . . . . . . . . . . . 262

7 Object Interaction 275


7.1 Class Hierarchies . . . . . . . . . . . . . . . . . . . . . . . . 276
7.2 Inheritance and Variables . . . . . . . . . . . . . . . . . . . 279
7.3 Inheritance and Methods . . . . . . . . . . . . . . . . . . . 284
7.4 Using super and instanceof . . . . . . . . . . . . . . . . . 288
7.5 Polymorphism and the abstract Modifier . . . . . . . . . . 293
7.6 Avoiding Errors and Debugging . . . . . . . . . . . . . . . . 296
7.7 Review Exercises 7 . . . . . . . . . . . . . . . . . . . . . . . 300

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

10 Searching and Sorting 381


10.1 Sequential Search . . . . . . . . . . . . . . . . . . . . . . . . 382
10.2 Binary Search . . . . . . . . . . . . . . . . . . . . . . . . . . 385
10.3 Insertion Sort . . . . . . . . . . . . . . . . . . . . . . . . . . 389
10.4 Selection Sort . . . . . . . . . . . . . . . . . . . . . . . . . . 392
10.5 Bubble Sort . . . . . . . . . . . . . . . . . . . . . . . . . . . 395
10.6 Shellsort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 400
10.7 Comparing Algorithms . . . . . . . . . . . . . . . . . . . . . 404
10.8 Comparing Objects . . . . . . . . . . . . . . . . . . . . . . . 411
10.9 Review Exercises 10 . . . . . . . . . . . . . . . . . . . . . . 419

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

12 Dynamic Data Structures 477


12.1 Linear Lists and Linked Lists . . . . . . . . . . . . . . . . . 478
12.2 More Operations on Linked Lists . . . . . . . . . . . . . . . 487
12.3 Stacks and Queues . . . . . . . . . . . . . . . . . . . . . . . 492
12.4 Recursive List Processing . . . . . . . . . . . . . . . . . . . 498
12.5 Binary Trees . . . . . . . . . . . . . . . . . . . . . . . . . . 503
12.6 Binary Search Trees . . . . . . . . . . . . . . . . . . . . . . 511
12.7 Avoiding Errors and Debugging . . . . . . . . . . . . . . . . 515
12.8 Review Exercises 12 . . . . . . . . . . . . . . . . . . . . . . 517

13 Graphical User Interfaces 523


13.1 Drawing Text . . . . . . . . . . . . . . . . . . . . . . . . . . 524
13.2 Drawing Simple Shapes . . . . . . . . . . . . . . . . . . . . 532
13.3 Layout Managers . . . . . . . . . . . . . . . . . . . . . . . . 537
13.4 Button Events . . . . . . . . . . . . . . . . . . . . . . . . . 548
13.5 Mouse Events . . . . . . . . . . . . . . . . . . . . . . . . . . 555
13.6 Text Input and Output . . . . . . . . . . . . . . . . . . . . 564
13.7 Avoiding Errors and Debugging . . . . . . . . . . . . . . . . 572
13.8 Review Exercises 13 . . . . . . . . . . . . . . . . . . . . . . 573

A The Classes In and Out 577

B Input and Output 585

C Characters and Integers 593

D HTML and Applets 603

E Exception Handling 623

viii
F The Javadoc Program 631

G Java Operators 639

H Answers to Exercises 649

Index 672

ix
Preface

This book is intended to provide a student who is completely new to com-


puter science with an introduction to the Java programming language and
techniques for problem solving using a computer. Although a great deal of
space in the book is devoted to features of Java, the book does not cover
the language completely. Instead, it focuses on those aspects of the lan-
guage that are needed to introduce ideas about problem solving. Java is a
means to this end rather than the end in itself.
Although many books using Java have been published in the past few
years, I naturally like to think that this text has something new to offer.
Here are some of the features that I hope will help to enhance the usefulness
of the book.

Early Introduction to Programming


Based on the idea that students learn how to program by writing their own
programs, the text introduces programming almost immediately. Students
will be able to write simple Java programs after completing the second
section of Chapter 1.

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.

Input and Output


In order to simplify reading of data, Appendix A contains a simple class
called In that can be used to read numbers, characters, or strings from the
standard system input device. The advantage of this is that it allows one
to defer or avoid the use of Java’s rather challenging collections of objects
and methods needed for input. The disadvantage is that the class In is
not part of standard Java. If you want to avoid the use of In, Appendix B

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.

Paths Through the Book


The first six chapters should probably be studied in sequence but not every
topic need be covered. In particular, combined operations with increment

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:

• At least an introduction to arrays in Chapter 8 is required to study


searching and sorting in Chapter 10.

• Some of the basic ideas of inheritance in Chapter 7 are useful for


understanding graphical user interfaces in Chapter 13.

• The discussion of complexity in Section 10.7 should be done before


the more advanced discussion of this topic in Section 11.7.

• A basic knowledge of recursion from Chapter 11 is required for the


material on recursively defined data structures in the last half of
Chapter 12.

Further flexibility in ordering of topics is provided by the appendices which


contain material that different instructors may want to teach at different
times (or not at all). As one example, it may be desirable to teach the
material on input and output (Appendix B) immediately after Chapter 1.
As another example, the topic of exception handling (Appendix E) can be
taught at any time or omitted completely.
The text contains more material than can be covered in a single intro-
ductory computer science course. Where I have included non-core topics,
I have tried to be careful not to make such material integral to further
study. For example, the book examines many sorting techniques, allowing
instructors to choose which one(s) they want to study. Subsequent chapters
do not require the knowledge of any particular sorting algorithm.

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

We begin our study of computer science with some basic


ideas about computer systems and the process of creat-
ing programs written in a language called Java. We then
turn our attention to writing Java programs, beginning
with very simple ones that write messages. Later in the
chapter we see how to create variables that can be given
values of various types, representing numbers, charac-
ters, or sequences of characters (called strings) and we
see how to read and write such values.
2 CHAPTER 1. GETTING STARTED

1.1 Introduction

This book is about problem-solving using the Java programming language.


It is not primarily a book about how computers work. Having some ba-
sic ideas about how computers are organized will, however, be useful in
understanding how to use a computer to solve problems. Thus, before we
start looking at problem-solving, we are going to take a brief look at the
structure of a computer system and how Java can be used with a computer
system. This section contains a large number of terms that may be un-
familiar. Do not worry too much, for now, about the terminology. Read
through the section trying to understand as much as you can and then,
once you have written a few Java programs, come back and re-read it. It
should make more sense once you have actually used a computer system a
few times.
A computer system can be divided into two main parts: hardware and
software. The hardware consists of all the equipment that is involved in op-
erating a computer. It can include keyboards, screens, CD drives, speakers,
modems, and printers. The software consists of sets of instructions, called
programs, that are used to tell the computer what tasks it is to perform.

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

Main Storage Input/Output


(memory) Devices

The central processing unit (CPU) is the heart of a computer. It con-


sists of two main parts: a control unit and an arithmetic-logic unit (ALU).
1.1. INTRODUCTION 3

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

(a) a computer system,


(b) a computer,
(c) a CPU.

2. Which part of a computer would perform each of the following tasks?

(a) add 2533 and 4178


(b) print a line of text
(c) determine whether one number is larger than a second number
(d) store the result of a calculation
(e) send an item from memory to an output device

3. How is information organized in auxiliary storage units?

4. Why are programs rarely written in machine code?

5. Explain the difference between a source program and an object pro-


gram.

6. What is the primary difference between the object code produced by


most compilers and the byte code produced by the Java compiler?
6 CHAPTER 1. GETTING STARTED

1.2 A First Program

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");
}
}

Let us look in detail at the components of the program in Example 1.

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.

4. // This program ...


The two adjacent slashes indicate a comment in a Java program.
Anything on a line after // is ignored by the compiler. Although
they do not affect the way that the program works, comments can
be very useful to any person trying to understand a program. A
comment can also be indicated in a Java program by using /* to
start the comment and */ to end it. Using this style, we could have
written our comment as
/* This program prints a simple message */
Usually, comments that appear entirely on one line are written using
the // style while those that occupy multiple lines are written using
the /* ... */ style.

5. public static void main (String[] args)


Java programs consist of one or more methods, (sometimes called
functions or procedures in other programming languages). If the pro-
gram is run directly by the Java interpreter, then exactly one of its
methods must be called main.1 Our program has only one method so
it must be called main. This line is the header of the main method.
The other words and symbols in the header will be explained later.
For now, just use them, exactly as shown.

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

Notice the ways in which blanks are used in Example 2.

1. At least one blank must be used to separate two adjacent words.


Thus, classGreet or voidmain would be incorrect.

2. If a symbol appears between two words, it can be used as a separator


so that a blank is no longer required. For example, main(String and
main ( String are equivalent.

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.

A number of variations can be made to our basic printing technique.

1. To print a blank line, we can simply write


System.out.println();

2. If we want to print a string that is longer than we have room for on


one line of our program, we cannot simply continue the string onto
a second line. If we were to do so, the compiler would get upset and
refuse to compile our program. Instead, to print a very long string,
we can write something like the following:

System.out.println("This will print a verrrrrry long"


+ " string on one line by joining"
+ " these strings into one string");

The process of joining strings is called concatenation. The plus sign


used with strings acts as the concatenation operator in Java.

3. If we do not wish to jump to a new line after printing a string, we


can use the method print instead of println as illustrated in the
next example.
10 CHAPTER 1. GETTING STARTED

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

what you can do the day

after tomorrow.

Since the beginning and end of a string to be written by print or


println are marked by double quotes, how do we print a string that con-
tains double quotes? Java’s solution is to use a backslash character, \, im-
mediately in front of a double quote to signal the computer that it should
print the character ".

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

The technique used to print a double quote can be used in a number


of other ways. For example, to print a backslash, you should write \\.
To jump to a new line, you should write \n. (The n indicates a new line.)
These pairs are called escape sequences. A complete list of escape sequences
can be found in Appendix C.

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");
}
}

2. Rewrite the following program using the indentation style shown in


the text.

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


System.out.print("The unexamined life");
System.out.println(" is not worth living.");}}
12 CHAPTER 1. GETTING STARTED

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?’)
}

5. Write a Java program to print your name in giant letters as shown


in the following example.

BBBB EEEEE A TTTTT RRRR III X X


B B E A A T R R I X X
B B E A A T R R I X X
BBBB EEE AAAAA T RRRR I X
B B E A A T R R I X X
B B E A A T R R I X X
BBBB EEEEE A A T R R III X X

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

7. Write a complete Java program to print your name and address as


they would appear on the envelope of a letter written to you at your
home.
1.3. DEVELOPING JAVA PROGRAMS 13

1.3 Developing Java Programs

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.

Even if a program compiles correctly, it may not run correctly. If


this is the case, then once again the problem must be analyzed and the
program must be corrected in the editor, re-compiled, and re-run. This
cycle, sometimes called the program development cycle, is illustrated in the
next diagram.

The Program Development Cycle


Start

?
Edit
Source
Program

?
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.

2. To get some familiarity with Java’s reactions to mistakes in a pro-


gram, try introducing the following errors (one at a time) in the
program of Question 1 and then attempting to compile and run the
resulting program. Note the result in each case.

(a) Change Greet to greet.


(b) Change main to Main.
(c) Change println to write.
(d) Add a semi-colon after args).
(e) Omit the square brackets after the word String.

1.4 Integer Types

The basic unit of information in a computer is called a bit. It can be in one


of two states, usually designated as zero and one. Many physical devices
can also be in one of two states and, therefore, can be used to store bits of
information. As an example, on a compact disc, the presence or absence of
a very small pit in the disc indicates a one or a zero at a particular point.
A group of eight bits is called a byte. Since each bit can be in one of two
states, a byte can be in one of 2 × 2 × 2 × 2 × 2 × 2 × 2 × 2 = 28 = 256 states.
In Java, a byte can be used to store small integers: 27 negative values (from
−1 to −128) and 27 non-negative values (from 0 to 127). Notice that the
greatest positive number has a magnitude that is one less than that of the
least negative number. This is because zero is one of the 27 non-negative
values. An integer value that is stored in one byte is said to be of type
byte.
The concept of a type is an important feature of high-level computer
languages. It allows us to convey meaning to an otherwise meaningless
group of bits. In Java, the byte type is a group of eight bits that is to be
treated as an integer.
1.4. INTEGER TYPES 17

If we wish to store an integer value with a larger range than that


permitted by the type byte, we can use 16 bits. Integer values stored in
16 bits are said to be of type short. Their range is from −215 to 215 − 1.
Java permits two other types of integer values: int and long. The
following table gives a summary of the four integer types.
Type Size (in bits) Range Approximate Range
byte 8 −27 to 27 − 1 ±100
short 16 −215 to 215 − 1 ±30 000
int 32 −231 to 231 − 1 ±2 000 000 000
long 64 −263 to 263 − 1 ±9 × 1018

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).

Integer constants in Java are written as a sequence of decimal digits


with or without a preceding plus or minus sign. They should not be written
with any leading zeros.3 An integer constant is normally stored as an int
value. If the letter L or l is placed at the end of a constant it is stored as
a long value.

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

3. What is the smallest type of integer value that would be required to


store each of the following integer values?
(a) 50 000 (b) −3 000 000 000
(c) −125 (d) 128

4. State, with reasons, which of the following are not legal Java integer
constants.
(a) -47 (b) 23.
(c) -0 (d) 22 900

5. In some computer languages (although not in Java), groups of bits


can be used to represent unsigned integers whose least value is zero.
In such a representation, what is the largest integer that could be
stored as an unsigned integer using 32 bits?
1.5. OTHER PRIMITIVE TYPES 19

1.5 Other Primitive Types

In addition to the four integer types discussed in the previous section,


Java has four other primitive types of values: float, double, char, and
boolean. We will examine them in this section.
To store numeric values containing decimals or to store integers whose
size exceeds the largest value that can be stored in a long integer, Java uses
two floating point types: float and double. In both of these types, the
numbers are represented in a form that is similar to scientific notation. In
scientific notation, the value 576.34 is written in the form 5.7634 × 102 . In
a floating point representation, the number is separated into two parts: the
mantissa (the digits) and the exponent. The next table gives a summary
of the two floating point types.

Type Size (in bits) Precision Approximate Range


float 32 at least 6 ±3.4 × 1038
1 for sign decimal digits
8 for exponent
23 for mantissa

double 64 at least 15 ±1.8 × 10308


1 for sign decimal digits
11 for exponent
52 for mantissa

Floating point constants in Java can be written as an optional plus or


minus sign, followed by a sequence of one or more digits together with a
decimal point. A floating point constant is normally stored as a double
value. If the letter F or f is placed at the end of a constant, it is stored
as a float value. In the past, memory was more costly and operations on
float values were much faster than those on double values. The situa-
tion has improved since then and now the type double is almost always
used for floating point values. Although double values can represent a
wide range of numbers to a very high precision, you should realize that
they cannot represent every real number; to do so would require an infinite
20 CHAPTER 1. GETTING STARTED

number of bits. Instead, during calculations, values are rounded if neces-


sary. Although rounding errors can become critical in some situations, for
our purposes, double values will be adequately precise.

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.

Floating point constants can also be written in a form similar to that


used in scientific notation with one part showing the digits and the other
showing the position of the decimal point. The system is illustrated in the
next example.

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

Groups of bits can also be used to represent characters. Early com-


puters used six bits to represent a character, permitting a total of 26 = 64
different characters. With this scheme, only the 10 digits, 26 upper case
letters of the alphabet, and a variety of punctuation marks and other sym-
bols could be represented. The details of the coding scheme varied from
computer to computer. More recently, a seven bit encoding called ASCII
(American Standard Code for Information Interchange) has gained wide
acceptance, allowing the use of both upper and lower case characters and
far more special characters.
The creators of Java have chosen a newer representation called Unicode
that uses 16 bits to represent each character. With 16 bits, a total of
216 = 65 536 characters can be represented. With such a large number of
character codes available, the characters and symbols used by any of the
1.5. OTHER PRIMITIVE TYPES 21

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’

To represent the apostrophe character, we use an escape sequence, just


as we did inside strings (in the previous section). Thus, the constant that
represents the single apostrophe can be written in the form ’\’’. Similarly,
the newline character (that causes a jump to a new line) can be written as
’\n’.
It is also possible to represent a character by specifying the bit pattern
as a hexadecimal value in a Unicode escape sequence. For example, the
character Σ can be specified by writing \u3a03. If you are interested in
exploring this further, see Appendix C. You do not need to know anything
about this form of character representation in order to follow any of the
material in this text.
The last of Java’s eight primitive types is called boolean. The boolean
type represents a truth value. There are only two boolean values: true
and false. These words, true and false are reserved by Java for boolean
values and cannot be used in any other way. Boolean values are represented
using one bit.

Exercises 1.5
1. Rewrite in standard decimal form.
(a) 2.94e1 (b) 0.0004E3
22 CHAPTER 1. GETTING STARTED

(c) -2e-3 (d) 26.77e-3


(e) -54E-3 (f) -.3e1

2. The double constants 6.73 and 67.3e-1 would be stored identically


in Java. Find three other representations of each of the following
double values.
(a) 27E4 (b) -0.0058e2
(c) -87.69 (d) -3e-4

3. Identify the type of each constant.


(a) -3.8e2 (b) 53L
(c) -12 (d) false
(e) 17.4 (f) 2E3
(g) .5f (h) ’2’

1.6 Identifiers and Variables

If we are to create programs that do something other than write messages,


we will need some way of storing and retrieving values. The memory cells
used to store values that can change during the execution of a program are
known as variables. In order to manipulate values in Java, we create iden-
tifier names (or, more simply, identifiers) for our variables, the methods
that contain them, and the classes that contain the methods. The rules for
creating any type of identifier are:

1. Any character used in an identifier must be a letter of the alpha-


bet (either upper case or lower case, in any alphabet supported by
Unicode),4 a digit (0, 1, . . . , 9), the underscore character, or a Uni-
code currency symbol such as $, £, or = Y. Java distinguishes between
upper case and lower case letters so that, for example, x and X are
recognized as different characters.
4 This includes accented Latin letters such as é, ö, and č. It also includes Greek,

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

2. The first character cannot be a digit.


3. An identifier that you create must not be one of the following reserved
words, each of which has a pre-defined and unalterable meaning in
Java.
abstract assert boolean break byte
case catch char class const
continue default do double else
enum extends false final finally
float for goto if implements
import instanceof int interface long
native new null package private
protected public return short static
strictfp super switch synchronized this
throw throws transient true try
void volatile while

In addition to these rules that must be followed in creating identifiers,


there are also a number of conventions that should be used.

1. Currency symbols (such as dollar signs) in identifiers are used by


system programs and should be avoided by users.
2. Long variable or method identifiers are written primarily using lower
case letters with either upper case letters or underscores used to make
the meaning clearer. For example, a variable to represent a starting
time could be timeAtStart or time at start. In this text, we will
use the combination of upper case and lower case letters, not the
underscore character.
3. Class identifiers start with an upper case letter; variable and method
identifiers do not. Other characters in class identifiers follow the
convention described for variables.

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

Names that should not be used as variable identifiers are:


this – a reserved word
second-time – contains a hyphen
3rdTime – starts with a digit
Last – valid but, by convention, variable identifiers start with a lower
case character
valueIn$ – valid but, by convention, currency symbols are avoided
word Length – contains a blank

It is good programming practice to use meaningful identifiers in your


programs. Although it takes a bit more work to type a name like highMark
than it does to type hm, the former gives the reader a much clearer idea
of the use to which the identifier is to be put. In large programs, it is not
unusual to have hundreds of different identifiers and, in these programs,
meaningful names are almost absolutely essential for reliability. Although
you will not likely be writing such large programs for some time, descriptive
names will improve the clarity of any program and should be used at all
times.
To reserve space in memory for variables, we must write a declaration
in which we specify the type of a variable and its identifier. A simple
declaration has the form
<type> <identifier>;
Note that the declaration, like any statement in Java, is terminated by a
semi-colon.

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

It is not necessary to use a separate declaration for each variable that


we want to create. We can request that space be allocated to many variables
of the same type in a single declaration using a statement of the form
<type> <identifier1 >, <identifier2 >, . . . , <identifierk >;

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.

length width height

When a variable is declared in a method, the declaration can appear


at the start of the method (just after the opening brace bracket) or it can
appear later. A variable must, however, be declared before it is first used.
The practice that we follow in this text is (usually) to declare a variable
just before we are about to use it.

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

(g) number-of-wins (h) ageDuGarçon

(i) average_age (j) This

3. Rewrite each fragment using the minimum number of declarations.


(a) int quantity; (b) double income;
double price; char taxCode;
int size; double rate;
double discount;

4. A program is to be written that records the results of a coin-tossing


experiment. The program will need variables to record the number
of heads, the number of tails, the percentage of heads (correct to four
significant digits), and the experimenter’s first and last initials. Write
declarations to reserve space in memory of the appropriate type for
each of these variables. Be sure to use identifiers that show clearly
the purpose of each variable.

1.7 Assigning Values to Variables

Declaring variables, as we did in the last section, simply reserves space in


the computer’s memory and associates that space with the identifier that
we have chosen; it does not give a value to that variable. If we were to
write a program that attempted to print the value of a variable after it had
been declared but before it had been given a value, Java would give us an
error message when we attempted to compile the program.
Once variables are declared, there are many ways in which we can give
values to them. One of the simplest ways is to use an assignment statement
of the form
<identifier> = <value>;

Although the form of an assignment looks like an equation, it is quite


different. The statement causes an action to be performed: the <value>
on the right side of the equals sign is stored in the location reserved for the
<identifier>. Note that, as with a declaration, an assignment statement is
terminated by a semi-colon.
1.7. ASSIGNING VALUES TO VARIABLES 27

When we first give a value to a variable, we say that we are initializing


the variable. Initialization of a variable can be done as soon as it has
been declared. In fact, a declaration and assignment can be done in one
statement. The next example illustrates this.

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

In any assignment of the form

<identifier> = <value>;

the type of <value> must be compatible with the type of <identifier>.


Normally this means that <identifier> and <value> are of identical types
but there are some exceptions.
Generally, Java permits conversions from one type to another if it
should be safe to make the conversion. For example, Java permits us to
assign a byte value to an int variable because any value that can be
represented by a byte variable (of 8 bits) can obviously be represented in
an int variable (of 32 bits). In Java, such a conversion is referred to as a
widening conversion because the new representation is wider (has a greater
range) than the old representation. The following table summarizes the
valid widening conversions permitted in Java.
28 CHAPTER 1. GETTING STARTED

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;

Java permits the assignment of integer constants to either short or


byte variables without using a cast.

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

short s = (short) 75000;


System.out.println(s);

will compile and execute without producing an error message. Unfortu-


nately, when it is executed, the value that it prints will be 9464!

If a cast is used to convert a floating point type to an integer type,


then any fractional part will be lost.

Example 5
The following fragment will store the value 87 in the int variable i and
-32 in j.

int i = (int) 87.65;


int j = (int) -32.999;

It is also possible to use a cast to convert numerical values to type


char. For technical reasons beyond the scope of this book, these are always
considered to be narrowing conversions, even conversions from byte to char
(where the number of bits is increasing) or from short to char (where the
number of bits is unchanged). On the other hand, as noted in the preceding
30 CHAPTER 1. GETTING STARTED

table, a char can be converted to a variety of numerical types. The result


of such a conversion is a non-negative integer value — the value of the
bit pattern of the character, if this pattern were considered to represent
a binary numeral. If you want to gain a better understanding of this, see
Appendix C.
Note that the type boolean is not mentioned in the list of widening
conversions. This is because variables of type boolean cannot be converted
to any of the seven other primitive types. It is also true that none of the
other primitive types can be converted to the type boolean.
The right hand sides of assignment statements do not always consist
of constant values. An assignment statement can also be used to copy a
value from one variable to another.

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;

State, with reasons, which assignments are illegal.


1.8. STRING VARIABLES 31

(a) number = 32.0; (b) mass = 37;

(c) density = 1.52e0; (d) symbol = +;

(e) age = 19; (f) limit = 10000;

2. Which of the following conversions always require a cast?


(a) int to char (b) byte to int

(c) double to float (d) float to short

(e) double to long (f) int to float

3. Write a program fragment that declares the short variable subTotal,


then declares the int variable grandTotal, initializing grandTotal
to zero, and finally copies the value from grandTotal into subTotal.

4. Find and correct the errors.


(a) short s = 25; (b) int i = 50;
int i = int(s); short s = i;

1.8 String Variables

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

To assign a value to a string variable, we can proceed in a manner


similar to that used for assignments to primitive types. The result of such
an assignment is, however, quite different from what we have seen with
primitive types.

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

actually contain a string. Instead, it contains a reference to the actual


string object which is stored elsewhere in memory. Because s contains
a reference to a string, we say that it is an example of a reference type
variable (as opposed to a primitive type variable that actually contains its
value).
Once we create a string, its value cannot be changed. Because of this,
we say that strings are immutable objects. This does not mean that we
cannot change values of variables that refer to strings.

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"

Although the string containing "Joanne" still exists, it is no longer acces-


sible.

As well as assigning constants to string variables, we can also assign


one string variable to another. The result is similar in some ways to that
seen with assignments involving variables of primitive types: the value
stored in one variable is copied into the other variable. Now, however, the
copying involves references and so the effect is quite different, as shown in
the next example.
34 CHAPTER 1. GETTING STARTED

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";

(c) String a = "one"; (d) String a = "ein";


String b = a; String b = "zwei";
a = "two"; a = b;
b = "drei";

2. What would be printed by the following fragment?

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);

1.9 Printing and Reading Values of Variables

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

public static void main (String[] args)


{
int number = 37;
System.out.println(number);
}
}

Of course, we must be sure that variables have values before we at-


tempt to print them.

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);
}
}

In the statement System.out.println("Hello"); the string "Hello"


contained within the parentheses is called the argument of the println
method. The print and println methods both print strings. If we give
an argument that is not a string, it must be converted to a string. For all
of Java’s primitive types, this conversion is done automatically.
It is also possible to use a mixture of strings and variables of other
types as the argument of the print or println methods. In this case,
Java will, again, automatically convert any non-string values to strings.
Then the concatenation operator (+) can be used to produce one string for
printing.
1.9. PRINTING AND READING VALUES OF VARIABLES 37

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

It would be better to write


System.out.println("Dimensions are " + height
+ " and " + width);
to print
Dimensions are 4 and 6

As the previous examples illustrate, integer values are converted to


strings of just the right length before printing. To print boolean variables,
the strings "true" or "false" are generated. For char variables, a one-
character string is produced. For floating point variables, the values are
normally converted to strings containing 8 digits for float values and 16
digits for double values. However, any trailing zeros after a decimal point
will not be printed.
38 CHAPTER 1. GETTING STARTED

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

The process of reading values into memory is rather complicated in


Java. To make reading simpler, we have included with this text (in Ap-
pendix A and on our web site) a set of methods that allows you to read
values from the standard input device for your computer (likely the com-
puter’s keyboard), in a fairly simple way. The methods are contained in
a class called In. (If you prefer to use Java’s methods for reading values,
instructions for their use can be found in Appendix B.)
The class In is not part of Java; you will not likely see it used outside
this text. To use the methods contained in In, obtain a copy of the file
In.java, copy it into the directory that you are using for your Java pro-
grams, and then compile it, just as you would compile any Java program,
to produce a file called In.class. Now, to read a value of a particular
type, simply write the name of the appropriate method in an assignment
statement as shown in the next example.
1.9. PRINTING AND READING VALUES OF VARIABLES 39

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

System.out.println("What is your family name?");


String familyName = In.getString();
System.out.println("What is your age in years?");
int age = In.getInt();

// respond to input
System.out.println("Well, " + firstInitial + ". "
+ familyName + " - "
+ "I see that your age is " + age);
}
}

If a user were to respond to the prompts with

N
Fung
17

then the program would produce the following output:


Well, N. Fung - I see that your age is 17

Exercises 1.9
1. What would be printed by each fragment?

(a) int i = -47, j = 35;


System.out.println("The value of i is " + i
+ "\nwhile j is " + j + ".");
(b) boolean done = false;
System.out.println("We are not " + done + " yet");
(c) double x = 0.012, y = 2.7374e1;
System.out.println("x -> " + x + "\ny -> " + y);

2. Write a program that creates a double variable called myMass, asks


the user to give his/her mass, and then prints a message giving the
person’s mass.
1.10. CONSTANTS 41

1.10 Constants

In our programs so far, we have seen a variety of constant values. Some of


the constants that we have used were numeric, either integers or floating
point values:
-453, 4.8E3, -87.102, 9876
Others were char constants, characters surrounded by apostrophes:
’!’, ’\\’, ’4’
Still others were the two boolean constants:
true, false
Constants (of any type) like those shown here, are often referred to
as literal constants or simply literals. A literal, roughly speaking, is some-
thing that represents itself. Java can deduce a literal’s type and value by
examining the characters used to write the literal.
Java also allows us to associate an identifier with a constant value
through the use of the final modifier in the declaration of a variable.

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

Why would we want to have identifiers associated with constants?


Why use named constants instead of literals throughout our programs?
For one thing, named constants make programs easier to understand.
If someone else were to read one of your programs (or if you were to read
it some time after you had written it), it would likely be much easier to
understand if it used a name like VOTING_AGE rather than the literal value
18.
Another reason is that the use of named constants can often make
revisions to programs easier. Suppose, for example, that we have written
a fairly large program to keep various records of the progress of the stu-
dents in a computer science class. Suppose further that the program was
originally written for a class of 30 students but is now to be revised to ac-
commodate a class of 40 students. If we had used literals in our program,
we might have to change 30 to 40 in many places in the program. If we
had used a named constant, we would only have to make one change — in
the declaration of the constant.
A third reason for their use is to help programmers avoid errors. Sup-
pose again that we are revising our class record program to accommodate
a larger class. If we have used literals and we change every occurrence of
30 to 40, we might not just increase the class size to 40 but also make
some changes that we did not want. We might, for example, inadvertently
change the percentage allocated to term work from 30% to 40%.
Literal constants in programs are often referred to as magic numbers.
Just as we are left wondering how a magician makes a rabbit appear out of
nowhere, we might be equally in the dark as to why a variable was assigned
the value 12. Generally speaking, one should try to avoid magic numbers.
If you are not convinced by the arguments against them that we have given
here, perhaps the fact that many instructors take marks off programs that
contain magic numbers will bring you over to our point of view.
As well as allowing us to create our own named constants, Java also
provides us with some pre-defined named constants. Two of these are
important mathematical constants: π, a value that shows up in almost
every branch of mathematics, and e, a constant that appears frequently in
many areas of higher mathematics. Both of these exist as double values:
the value of π is stored in Math.PI while the value of e is stored in Math.E.6

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);
}
}

1.11 Avoiding Errors and Debugging

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.

3. Use comments to explain your code. A short comment just before a


section of code will be a helpful reminder later on of the purpose of
that section of code.

4. Avoid cluttering up your program with too many comments. It is


neither necessary nor desirable to comment every line of your pro-
gram. Comments are supposed to assist readers in understanding a
program’s actions, not to distract them.

5. Use blank lines to separate sections of a program. If logical units of


a program are visually isolated from each other, a reader can see the
structure and absorb its meaning a chunk at a time.

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

1. If a program fails to compile, start your correction process by reading


the first error message produced by the compiler. A single error can
cause the production of many error messages with later ones being
written because the original error confused the compiler.
2. It is extremely difficult to have the compiler produce the “correct”
message for every possible error condition, but usually the message
will be reasonably informative. Read the message very carefully to
see what the compiler is trying to tell you. Once you have corrected
an error, try to memorize the message that it produced. The next
time that you see that message, you should be able to correct the
error more quickly.
3. A common slip is the failure to put a semi-colon at the end of a state-
ment. Since Java’s compiler ignores a line break after a statement,
it will not detect this error until it reaches the next line. The error
message will then be associated with that line. If you get an error
message that directs you to one location and you cannot find any-
thing wrong on that line, look at the previous line to see if something
there is the cause of your problem.
4. Compare your program with examples of similar programs that are
correct. Look at the syntax that your program uses and compare this
with the code of the working program.

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.

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


System.out.println("In what year were you born?");
46 CHAPTER 1. GETTING STARTED

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);
}
}

4. To gain more familiarity with error messages that will be produced


by the Java compiler, take a correct program that you have written
and then introduce each of the following errors in that program (one
at a time), noting any error message(s) produced in each case.
(a) missing the declaration of a variable
(b) attempting to print the value of a variable that has been declared
but not initialized
(c) attempting to change the value of an identifier that has been
declared with the final attribute
(d) using a reserved word as a variable identifier
(e) attempting to make a narrowing conversion without the use of
a cast
(f) omitting the */ required to close a comment that begins with
/*
(g) omitting the first semi-colon in the program
(h) omitting an opening brace bracket
(i) omitting a closing brace bracket
1.12. REVIEW EXERCISES 1 47

1.12 Review Exercises 1

1. What will this program print?

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.

The handsome stranger said,


"My name is Kim, Paul Kim."

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

4. What is the smallest type of integer that could be used to represent


each of the following values?
(a) 400 (b) 50 000
(c) −100 (d) 3 000 000 000

5. Rewrite in standard decimal form


(a) 8.77e-4 (b) 299.04e0
(c) -0.0443E6 (d) -23e-4

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

7. Which of the following conversions always requires a cast?


(a) long to double (b) byte to char
(c) float to double (d) byte to long
(e) short to char (f) double to long

8. What would be printed by each fragment?


(a) double x = 0.093e-1, y = -3e-3;
System.out.println("x: " + x + "y: " + y);
(b) int i = 5, j = -77;
System.out.println("first is "+i+"\nsecond is "+j);

9. The following program contains five known syntax errors. Rewrite


the program with those errors corrected.
class BadNews
/* another mess */
{
public static main (String[] args);
{
int i = 7; j = 3;
System.out.print("Smaller is " + i);
System.out.println(" Larger is " + j)
}
1.12. REVIEW EXERCISES 1 49

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

Programs That Calculate

Java has a variety of operators to perform arithmetic.


These include the usual basic ones seen in mathematics
along with some others that may be new. For mathe-
matics beyond elementary operations, Java has a large
collection of methods. In this chapter, we examine all of
Java’s arithmetic operators and many of its mathemati-
cal methods.
54 CHAPTER 2. PROGRAMS THAT CALCULATE

2.1 Basic Arithmetic Operations

Many arithmetic expressions in Java look much like ordinary arithmetic


expressions that you would see in a mathematics text. The four basic
arithmetic operations are shown in the following table.
Operation Java Operator
Addition +
Subtraction -
Multiplication *
Division /
The normal precedence of operations applies, with multiplication and
division being done (from left to right) before addition and subtraction
(again, from left to right). Parentheses (but not square or brace brackets)
can be used to change the order of evaluation. The multiplication symbol
cannot be omitted as it sometimes is in mathematics. Operators can be
written with or without extra spaces around them. Java also permits the
use of unary plus and unary minus in which a + or - is placed in front of
an expression. The unary plus and unary minus operators have a higher
precedence than any of the other arithmetic operators.

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

Whenever integer and floating point values are mixed in an expression,


the floating point values are contagious so that the result is always a floating
point value. In addition, if different integer types are mixed, the result is
that of the widest type. The same is true if floating point types are mixed:
the result of combining float and double values is always double.

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

(b) 1.0+1/2 ⇒ 1.0+0 ⇒ 1.0

(c) 3/4*2.0 ⇒ 0*2.0 ⇒ 0.0

In mathematics, division by zero is undefined. If we are dividing


one integer by another and the divisor has the value zero, then Java ob-
jects to this by throwing an exception. Many types of exceptions can be
thrown by Java, depending on the error. For this error, Java throws an
ArithmeticException. Throwing an exception has been likened to throw-
ing up your hands and saying “I can’t cope with this”. If any exception
is thrown during the execution of a program, then the programmer can
write instructions to catch the exception. Appendix E contains notes on
handling exceptions gracefully. For now, if a program contains this error,
Java will throw an ArithmeticException and the program will terminate
prematurely after printing a message stating the form of the exception.
If either operand of a division is a floating point value and an attempt
is made to divide by zero, Java does not throw an exception. To handle
results of such an operation, Java has three special floating point values:
positive infinity, negative infinity, and NaN (not a number). The result
positive infinity is produced by attempting to divide a positive value by
zero while negative infinity is produced by attempting to divide a negative
value by zero. These two values can be used in subsequent calculations.
For example, adding a non-infinite value to positive infinity produces pos-
itive infinity. The value NaN is produced if we attempt to divide zero by
zero. Attempts to print expressions having these values produce the strings
Infinity, -Infinity, and NaN.

Example 5
(a) 1.0/0 Result is Infinity

(b) -2.5/0.0 Result is -Infinity


2.1. BASIC ARITHMETIC OPERATIONS 57

(c) 0/0.0 Result is NaN

Another useful arithmetic operator is written as %. The expression


m % n gives the remainder after m is divided by n. We sometimes refer to
m % n as m modulo n (or simply m mod n). If either operand is negative,
the value of m % n is found by finding the value of |m| modulo |n| and
then taking the sign of m. The % operator is usually used with integers
but, in Java, it can also be used with floating point values. If the second
operand is zero, then Java throws an exception for integer values but the
expression evaluates to NaN for floating point values. In expressions with a
number of different operators, the % operator has the same precedence as
multiplication and division.

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

(a) 2 + 7 / 2 (b) 5(6 + 3.0)


(c) 2e02 - 02 (d) 3 * 5 / 2
(e) 3.0 * 5 / 2 (f) 2 * (3 / 4L)
(g) 2.0 + 7 / (3 / 4) (h) 3f * 4 / 5
(i) 35 / (5 / 4) (j) 4 - [6 - (2 - 7)]
(k) 3 / 4 * 2.0 (l) 3 / (4 * 2.0)

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)

3. Delete any unnecessary parentheses.


(a) ((a * b) / (c + d))
(b) ((a * b) - (c % d))
(c) ((a - b) % c) / (d * (e + f))
(d) ((a * b) - (c / d) - (e / (f / g)))
4. Write as Java expressions.
x
(a) + 4(y − 3) (b) x3 + y3
2
x+1 x
(c) 2 +z
y−2 y
(d) y
x+
z

5. Use the symbols +, -, *, /, %, (, and ) as often as you wish, together


with the numerals 1, 8, 6, and 7, each used once, in the given order,
to create Java expressions whose values are 0, 1, . . . , 10.
As an example, to obtain the value zero, we could write

1 / 8 + 6 / 7⇒0 + 0⇒0
2.2. ASSIGNING AND PRINTING EXPRESSIONS 59

2.2 Assigning and Printing Expressions

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

We can use the print or println methods to print values of expres-


sions, with or without accompanying strings. In using arithmetic expres-
sions as parts of the argument to print or println, we must be careful of
the fact that the operator + has different meanings for string expressions
(where it means concatenation) and numerical expressions (where it means
addition).1 Whichever way we use +, it has the same precedence as the
arithmetic operator and if there is more than one occurrence of + in an
expression, evaluation takes place from left to right unless parentheses are
used to alter the order of evaluation. If + is used between a string and a
primitive value, the primitive value is automatically converted to a string
and then the two strings are concatenated. Using these rules, Java forms
a single string argument for print or println and then prints this string.
The next example illustrates the use of + in a variety of contexts.

Example 2
Suppose that the following declarations have been made:
int m = 3, n = 4;

The following table shows the output of a variety of statements involving


these variables. Can you explain each line of output?

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)

(c) 6 / 5 % (double) 2 (d) 2*(double)(7/4)

(e) (int)6.3 - (int)4.7 (f) (int)6.3 - 4.7

(g) (int)(6.3 - 4.7) (h) (double) 3 * 5 / 0

(i) (float)2*3 + 0.1 (j) (double) (7 % 4 / 3)

2. Assume that a program contains the following declarations.


int i = 5, j = 2;
What will be printed by each statement?

(a) System.out.println(i + j + 2);


(b) System.out.println(i + " + " + j + 2);
(c) System.out.println(i * j + 2);
(d) System.out.println("i * j " + 2);

3. Assume that a program contains the following declarations.


int i = 4, j = 7;
What will be printed by each statement?

(a) System.out.println("i " + i);


(b) System.out.println(i + j);
(c) System.out.println("i + j -> " + i + j);
(d) System.out.println(i + j + " <- i + j");
(e) System.out.println("i * j -> " + i * j);
62 CHAPTER 2. PROGRAMS THAT CALCULATE

2.3 Increment and Decrement Operators

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

2. If a variable has a postfix operator, the variable is incremented or


decremented after the old value has been used as the value of the
expression.
Increment and decrement operators have a higher precedence than any
of the other arithmetic operators. When combined with other arithmetic
operators, increments and decrements are performed first unless parenthe-
ses change the order of operations.

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:

Evaluation Stage Expression Value i j


Start i = (j++ + 4) * ++j 2 3
Evaluate j++, increment j i = (3 + 4) * ++j 2 4
Increment j, evaluate ++j i = (3 + 4) * 5 2 5
Add 3 plus 4 i = 7 * 5 2 5
Multiply 7 by 5 i = 35 2 5
Assign 35 to i 35 35 5

The final values of i and j are 35 and 5 respectively.

Although it is perfectly correct to write expressions like those in Ex-


ample 2, we do not recommend it. In general, we suggest that you only use
these operators by themselves. This may involve a bit more writing but it
will likely lead to greater clarity in your programs.

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

(a) i++; (b) ++j;


j = --k; k = i++ + --j;

(c) j = ++i * i++; (d) k = i++/j--;

(e) i = ++k * j--; (f) k = (2 + i++)/(3 + ++j);

2. Suppose that the following declaration has been made:


int i = 0, j = 3;
Using these starting values in each part, find the value of each variable
after the given statements have been executed.

(a) i = ++j + j; (b) i = j + j++;

(c) i = ++j*2 + ++j*3; (d) i = ++j*3 + ++j*2;

(e) j = 3*j-- + 2*j--; (f) j = 2*j-- + 3*--j;

3. Rewrite as a single statement.

(a) x++; (b) ++x;


y = x - z; z = x*y;
z++; --z;

4. Rewrite as a sequence of three statements.

(a) m = n++ - p--; (b) p = ++n - m--;

5. Find the values of j and k after execution of the following fragment.


j = 0;
k = 0;
j = --j;
k = k++;
2.4. MORE ASSIGNMENT OPERATORS 65

2.4 More Assignment Operators

Java contains a number of assignment operators in addition to the basic


one that we have already seen. As an example, a statement of the form
<identifier> += <expression>;

is equivalent to a statement of the form

<identifier> = <identifier> + (<expression>);

This can be very useful whenever we want to add a quantity to the


value of a variable.

Example 1

(a) The statement n += 3; is equivalent to n = n + (3);

(b) The statement x += 2.5; is equivalent to x = x + (2.5);

(c) The statement m += n - 2; is equivalent to m = m + (n - 2);

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);

(b) The statement m /= n * p; is equivalent to m = m / (n * p);

(c) The statement n *= 5; is equivalent to n = n * (5);

Java permits the use of multiple assignments in one statement. In such


statements, the assignment operators are applied from right to left, rather
than from left to right.

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));

All types of assignment operators can be mixed in a multiple assign-


ment statement. All varieties of assignment operator have the same prece-
dence, lower than that of any other Java operator. As with simple assign-
ments, evaluation again takes place from right to left.

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.

Evaluation Stage Expression Value i j


Start i += j = 5 + 3 * 4 4 2
Multiply 3 by 4 i += j = 5 + 12 4 2
Add 5 plus 12 i += j = 17 4 2
Assign 17 to j i += 17 4 17
Assign 21 to i 21 21 17

The value 21 is then converted to a string and printed. As side effects of


the evaluation of the expression, the values of i and j have been set to 21
and 17 respectively.

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;

2. Suppose that, before each statement is executed, the values of the


int variables i and j are 4 and 7 respectively. Find their values after
execution of the statement.
(a) j += i; (b) i *= j;
(c) i -= j--; (d) i *= --j;
(e) j %= --i; (f) j /= ++i;
(g) i *= ++j + 2; (h) j *= i-- % 3;

3. Suppose that, before each statement is executed, the values of the


int variables i, j, and k are 3, 2, and 1 respectively. Find their
values after execution of the statement.
2.5. ARITHMETIC AND CHARACTERS 69

(a) i = j = k; (b) i = j += k;

(c) i *= j = k + 2; (d) i -= j += k++;

(e) i += j *= ++k + 1; (f) i %= j = k++ % 4;

4. State the error in each statement.


(a) i+3 += j; (b) i *= j - 2 = k;

2.5 Arithmetic and Characters

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’.

We must be very careful to obey Java’s type conversion rules when we


are doing arithmetic.

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’

(c) ’m’ - ’p’ (d) ’3’ - ’8’

2. State the value of each expression.

(a) (char)(5 + ’t’)


(b) (char)(’2’ + 3)
(c) (int)(’z’ - ’v’)
(d) (int)(’A’ + ’f’ - ’D’ - ’b’)

3. For each valid fragment, state what it does. If a fragment contains an


error, explain the nature of the error and rewrite the fragment with
the error corrected.
(a) char a = ’e’; (b) char b = ’4’;
a--; b = b - 2;

(c) int c = ’g’; (d) int d = ’7’;


--c; char dd;
c = c - ’k’; dd = d++;

(e) int e = ’q’ + 2; (f) char f = ’M’;


e++; --f;
char ee = e; int ff = f-- - ’J’;

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

2.6 Using Math Methods

We have already encountered the classes In and System where we found


methods for performing input and output. To assist us in doing mathemat-
ics beyond basic arithmetic, Java has a large group of methods contained
in a class called Math. In this section we will examine some of the most
frequently used methods in the Math class. To use any of the methods in
Math, we must identify them as being in that class by including the class
name as part of the method name. Each of these methods computes a
value; we often speak of this value as the value returned by the method.

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

4 See Chapter 5 for a more detailed discussion of arguments.


2.6. USING MATH METHODS 73

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

Math.round, Math.ceil, Math.floor


The Math.round method rounds a floating point value to the nearest
integer. It returns an int value if its argument is float and returns a long
value if its argument is double. The Math.ceil and Math.floor methods
both take a double argument and return a double value. Math.ceil re-
turns a double that has the smallest integer value that is not less than its
argument while Math.floor returns a double that has the largest integer
value that is not greater than its argument.
2.6. USING MATH METHODS 75

Example 5
(a) Math.round(2.7) ⇒ 3L (b) Math.round(4.5f) ⇒ 5

(c) Math.round(-2.13f) ⇒ -2 (d) Math.floor(21.94) ⇒ 21.0

(e) Math.floor(8.0) ⇒ 8.0 (f) Math.ceil(18.8) ⇒ 19.0

(g) Math.ceil(-5.47) ⇒ -5.0 (h) Math.ceil(-5.0) ⇒ -5.0

The Math.round method can be adapted to rounding to a precision


other than the nearest integer. To do this, we can first apply a scaling
factor to the original value, round the result to the nearest integer, and
then reverse the effect of the scaling.

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

System.out.println("Please supply a value for rounding");


inValue = In.getDouble();
roundedValue = Math.round(inValue/PRECISION)*PRECISION;
System.out.println("Input value: " + inValue);
System.out.println("Rounded value: " + roundedValue);
}
}
76 CHAPTER 2. PROGRAMS THAT CALCULATE

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

most programming languages, prefers to work in radians rather than de-


grees. To convert from one measurement system to another, you can use
the relationship
π radians = 180 degrees
The methods Math.sin, Math.cos, and Math.tan each take a single
argument, representing the radian measure of an angle. They return the
sine, cosine, and tangent of their arguments, as double values.

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

The methods Math.asin, Math.acos, and Math.atan determine values


of the inverse trigonometric functions: arcsin (sin−1 ), arccos (cos−1 ), and
arctan (tan−1 ). The method Math.asin returns, as a double value, the
measure in radians of an angle whose sine is the argument. The others
behave similarly.

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

Exponential and Logarithmic Methods


The class also contains the methods Math.exp for determining ex , and
Math.log for determining ln x, the logarithm of x to the base e.

Example 9

(a) e = 2.7182818 . . . Math.exp(1) ⇒ 2.7182818. . .

(b) ln e = 1 Math.log(Math.E) ⇒ 1.0

(c) e3 = 20.0855369 . . . Math.exp(3) ⇒ 20.0855369. . .

(d) ln 1 = 0 Math.log(1.0) ⇒ 0.0

(e) ln(−1) is not defined Math.log(-1.0) ⇒ NaN

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

System.out.println("Give pendulum length in metres");


double length = In.getDouble();

// find period, rounded to nearest tenth of a second


double period = 2*Math.PI*Math.sqrt(length/G);
period = Math.round(10*period)/10.0;

System.out.println("For a length of " + length + " m,"


+ " pendulum’s period is " + period + " s.");
}
}

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

2. Write as Java expressions.



(a) a2 − b2 (b) π(x6 − y6 )
4 3
(c) 3 πr (d) |z 4 − 1|
(e) ln |1 + x| (f) x2 ex

3. A student incorrectly attempted to produce a random value uniformly


distributed over the set {1, 2, . . . , 6} using the expression
6*(int)Math.random() + 1
State and explain the actual value of the expression.

4. Write a statement that will make the int variable result take on a
random value uniformly distributed over the given set.

(a) {1, 2, 3, . . . , 10}


(b) {1, 2, 3, . . . , 52}
(c) {5, 10, 15, . . ., 100}
(d) {−5, −4, −3, . . . , 5}
(e) {100, 110, 120, . . ., 300}
(f) {a, a + b, a + 2b, . . . , a + kb} where a, b, and k are integers.

5. Write a statement that will assign the char variable randChoice a


random value from the set {’A’,’B’,’C’,’D’,’E’}.

6. Write a statement that will assign the double variable randVal a


random value from the set {1.00, 1.25, 1.50, . . . , 4.00}.
2.6. USING MATH METHODS 81

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))

9. Write Java expressions for each of the following.


(a) sin 45◦ (b) cos 120◦

(c) tan 24 (d) sec 18◦

(e) csc 160 (f) cot 15◦
(g) arctan 2 (h) arcsin 0.5

(i) cot−1 3 (j) sec−1 1.5

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

2.7 Avoiding Errors and Debugging

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

value should be exactly one but, because of roundoff error, the


value displayed by the calculator will likely be something like
0.999999999 Similarly, in Java, the statement
System.out.println(3.0*(1.0/3.0 + 100.0 - 100.0));
will not print 1.0 but only something close to it. Try it yourself
to see the result.
(b) Overflow errors occur when the magnitude of a value is larger
than the capacity of the storage location. Again, we can see this
with a calculator. By trying to evaluate the expression 100100 on
a calculator, the result that is displayed will probably be E (for
error) or something like that. In Java, double values can have
magnitudes that are up to approximately 10308 but if we try to
exceed that value we run into problems. If, for example, we try
to print the value of the expression 1E300 * 1E300, the result
that is printed will be Infinity since Java considers anything
greater than its largest value to be infinite.
(c) Underflow errors occur when the magnitude of a floating point
value is too close to zero. Java’s double values can have mag-
nitudes as small as (approximately) 10−323. Java considers a
value smaller than this to be equal to zero. For example, the
expression 1E-200 * 1E-200 will be stored as zero.

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.

6. Rounding errors can cause problems in the conversion of floating


point values to integers. For example, the value of the expression
(int)(100*9.87) is 986, not 987. The reason for this is that 9.87 is
stored in a form that is approximately equal to 9.869 999 999 999 998,
a representation that is very slightly smaller than the actual value.
When this is multiplied by 100, the result is 986.999 999 999 999 8
and, when this is cast as an int, the fractional part is lost. The rem-
edy for such problems is to use Math.round in conversions. Here, if
we write (int)Math.round(100*9.87), the result is 987. (The cast
is used to convert the value returned by Math.round from a long to
an int.)
84 CHAPTER 2. PROGRAMS THAT CALCULATE

7. If you are evaluating a number of expressions that have some common


elements, it is often a good idea to break up the calculations by first
evaluating the common sub-expressions and then using these values in
making the final calculations. This is both more efficient in computer
time and likely to make the resulting code more readable (and hence
less prone to errors). As an example, the area, A, of a triangle with
sides a, b, and c is
s    
a+b+c a+b+c a+b+c a+b+c
A= −a −b −c
2 2 2 2

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)

8. Although Java’s increment (++) and decrement (--) operators can be


combined with other operators in expressions, it is not usually a good
idea to do so. In your programming, you should always be aiming for
maximal clarity rather than maximal compression.
9. Although it may take a bit more writing, it is almost always better to
create new variables for new purposes, rather than re-using old ones.
For example, if we want to find the sum of x, y, and z, it is much
clearer to write
double total = x + y + z;
than it is to write
x += y + z;

Debugging

1. If a program is running but producing incorrect results, it is often


useful to trace its progress to help you find out where things are
going wrong. To perform a trace on a variable, note the points at
which the variable is assigned values and print those values, along
2.7. AVOIDING ERRORS AND DEBUGGING 85

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

(e) 1E-200 * 1E-200 * 1E200 * 1E200


(f) 1E200 * 1E200 * 1E-200 * 1E-200
3. A quadratic equation of the form ax2 + bx + c = 0 has roots

−b ± b2 − 4ac
2a
Write a fragment that efficiently determines the values of the roots
(root1 and root2) of the quadratic equation with coefficients a, b,
and c. Assume that all the variables have been declared as type
double and that the equation has two real roots.
4. Rewrite the statement
m = n-- * ++p;
using three statements.

2.8 Review Exercises 2

1. Evaluate. Use a decimal point in your answer if the result is double.


(a) 15 / 6 + 15 % 6 (b) 3e-1 + 2e1
(c) 2 / 5 * 8.0 (d) 1.6 * 20 % 8
(e) (double)(25/4) (f) (int) 2.7 + 6.3
(g) 20 - 10*(15%4) (h) 7/(-5) + 4%(-3)
(i) 2*3/(double)4 (j) (int) 4.8 % 1.1

2. Each of the following expressions is intended to evaluate the expres-


sion
ax2 + b
cx + d
Some are correct while others are not. Classify each as being correct
or incorrect. For those that are incorrect, give the reason(s).
(a) (a * x * x + b)/ ((c * x + d)
(b) (a * Math.pow(x,2) + b) / (c * x + d)
2.8. REVIEW EXERCISES 2 87

(c) ((a)(x)(x) + (b)) / ((c)(x) + d)


(d) (b + x * (x * (a))) / (d + x * (c))
(e) (a) * Math.pow(x,2) + (b) / ((c) * (x) + d)
(f) (a * (x * x)) + (b) / c * (x) + d

3. Suppose that the following declarations have been made.

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++;

4. State the value of each expression.


(a) (char)(’b’ + 6) (b) (int)(’M’ - ’T’)

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)

6. Write as Java expressions.


p p
(a) π|a2 − b2 | (b) 4
x6 + y 6

−b + b2 − 4ac 1
(c) (d)
2a a−1 + b−1

7. Write as Java statements.



1 (b) s = 1 + tan2 x
(a) y = 2
sin x − cos2 x
(c) t = ln |1 − x4 | 1 2
(d) y = √ e−x

88 CHAPTER 2. PROGRAMS THAT CALCULATE

8. To switch the values contained in the variables x and y, a programmer


wrote the following segment:
x = y;
y = x;

(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

integers; any remainders produced by divisions should be discarded.

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.

13. In order for an object to escape a planet’s gravitational pull, it must


attain a minimum initial velocity called the escape velocity. The
escape velocity varies from planet to planet but it is the same for all
objects on a given planet. Assume that we are analyzing the data
that a small probe has collected while exploring some mystery planet.
The probe has managed to obtain the circumference of the planet
and the acceleration due to gravity at the surface. The probe must
now determine what initial velocity it requires for takeoff in order to
remove itself from the planet’s gravitational force. You are to create a
Java program that will determine this velocity. Your program should
first prompt the user for the circumference of the planet and then
the acceleration due to gravity on the planet. From this information
your program should determine the radius, mass, and escape velocity
90 CHAPTER 2. PROGRAMS THAT CALCULATE

of the planet using the following equations.


r
2Gm Gm
vescape = a= 2
r r

In these equations, m kg is the planet’s mass, r km is the planet’s


radius, G is the gravitational constant approximated by 6.6726 ×
10−11m3 kg−1 s−2 , and a m/s2 is the acceleration due to gravity on
the surface of the planet. Your program should read input and print
output as shown in the following example. (The values shown after
the question marks are supplied by the user.)

Circumference (km) of planet? 38000


Acceleration due to gravity (m/s^2) on planet? 9.8

Calculating the escape velocity...


Planet radius: 6047.9 km
Planet mass: 5372.0 x 10^21 kg
Escape velocity: 10.9 km/s

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

Although useful work can be done with the statements


introduced up to now, for most problems the program
being executed by the computer must be able to make
decisions and take different actions depending on the
conditions that apply at the time. In this chapter we
examine two statements used by Java to make decisions:
the if statement and the switch statement.
92 CHAPTER 3. DECISION MAKING

3.1 Decisions and Relational Expressions

In order to make decisions, Java uses expressions of type boolean. Recall


that there are two values of type boolean: true and false. Just as we can
for other primitive types, we can create and use variables of type boolean.

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

One of the most common ways that we create boolean values is by


evaluating a comparison of other types of values.

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

• ’a’ < ’b’ < ’c’ < ...< ’z’


• A blank precedes any digit or letter.
• All digits precede all letters.
• All upper case letters precede all lower case letters.

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.

Types can be mixed in relational expressions just as they can in arith-


metic expressions. Any char values are compared to numerical values using
their Unicode encoding values. Values of type boolean cannot be compared
to values of any other type.
If arithmetic and relational operators are both included in an expres-
sion, the arithmetic operators all have a higher precedence than the rela-
tional operators.

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

2. State, with reasons, what this program will print.

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);
}
}

3. For each expression, state whether it is true or false.


(a) ’q’ < ’m’ (b) ’G’ > ’K’
(c) ’a’ < ’Z’ (d) ’5’ < ’v’
(e) ’q’ > ’7’ (f) ’9’ < ’ ’
(g) ’X’ < ’y’ (h) ’i’ < ’I’

4. Determine the value of each expression.


(a) 17 / 3 < 17 / 3.0 (b) ’F’ > ’B’ + 3
(c) -6 % 3 < 0 (d) (int) 1.1 * 0.9 <= 0
96 CHAPTER 3. DECISION MAKING

(e) (2 + 3 < 6) == true (f) (2 * 3 < 5) != true

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

Math.abs(x - y) < Math.abs(1e-3 * x)

will be true if and only if x and y differ by no more than 1/1000th of


the value of x.

(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?

3.2 Comparing Strings

The relational operators used to compare primitive types should not be


used to compare strings. The reason for this is based on the way that
string objects are stored in Java. Suppose that we have written
String s1 = "Hello";
String s2 = "Hello";

We can represent the result of executing these statements with a diagram.

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"

If we write the expression s1 == s2, Java evaluates this by comparing


the values stored in s1 and s2, not the strings to which s1 and s2 refer.
If s1 and s2 refer to the same copy of a string (as in our first case), then
s1 == s2 will have the value true but if they refer to different copies of
the same string (as in our second case), then s1 == s2 will have the value
false because s1 and s2 contain different memory references.
If we want to compare values of strings rather than the values of refer-
ences to those strings, we should use methods. Java provides a number of
methods for comparing strings. In this section, we will examine only two.
In Chapter 9, we examine strings in more detail.

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

(a) s.equals("Same") will return true

(b) s.equals("same") will return false because ’S’ 6= ’s’

(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.

• A character in one string differs from the corresponding character


in the other string; the string having the character earlier in Java’s
collating sequence precedes the other string.
3.2. COMPARING STRINGS 99

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 "

(g) "375" and "84" (h) "" and " "

2. State the value of each expression.


(a) "one".equals("one ") (b) "two".equals("2")

(c) "Three".equals("Three") (d) "four".equals("for")

3. State whether the value of the expression is negative, zero, or posi-


tive.

(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 ")

4. Suppose that s1 and s2 are variables of type String. Write an ex-


pression of the form s1.compareTo(s2) <op> 0 that will have the
value true if and only if the stated condition is true.

(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

3.3 The if Statement

In performing a task, we often want to take one of two possible actions,


depending on the conditions that exist at the time. For example

if you have homework, then


you should do it,
otherwise
you can go to a movie.

To accommodate this type of situation, Java uses an if statement


having a form similar to the conditional English statement shown above.

if (<boolean expression>)
<statement1 >
else
<statement2 >

The if statement’s action is controlled by an expression that must


be of type boolean. If <boolean expression> is true, <statement1 > is
executed; if <boolean expression> is false, <statement2 > is executed. In
either case, only one of the statements is performed.
Notice the parentheses that surround the <boolean expression>. These
parentheses are not optional. Without them, the syntax of the statement
is incorrect.
Notice also the way in which indentation is used in the if statement.
Each of the alternatives is indented slightly past the first line. As we
have said before, indentation is not required by the computer but, as the
structure of programs becomes more complex, indentation becomes more
and more important if we want to make the structure of our programs clear
to a human reader.
Some people find it useful to illustrate the action of statements with a
flow chart. As a program executes, we can imagine it travelling along the
paths of the flow chart, following the direction of the arrows. Here is a flow
chart for an if statement.
102 CHAPTER 3. DECISION MAKING

?
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

If the user supplies unequal values, the output will be


The values are not equal
3.3. THE IF STATEMENT 103

Sometimes a special action is necessary only if a certain condition is


true while no action is necessary if the condition is false. To accommodate
this type of situation, Java allows us to write an if statement that has no
else clause, as follows:
if (<boolean expression>)
<statement>
Here, <statement> will be executed if and only if <boolean expression>
is true. If <boolean expression> is false, the program simply proceeds to
execute the next statement in the program. The flow chart illustrates the
action of this form of the if statement.

?
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

If you always prefer to have else clauses in if statements, you can


still obtain the effect of an if statement without an else clause by using an
empty statement. An empty statement, as its name implies, has nothing in
it (except for its terminating semi-colon). Despite its strange appearance
(or lack of appearance), an empty statement can be used wherever any
other type of statement can be used.

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);

Frequently, when using if statements, we want to perform actions


that require more than a single statement. To do this, we can create a
compound statement or block, a group of zero or more statements enclosed
by brace brackets.

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.

2. Write Java statements to perform each task.

(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.

3.4 Boolean Operators

Just as arithmetic expressions can be acted upon by arithmetic operators


to produce new arithmetic expressions, boolean expressions can be acted
upon by boolean operators to produce new boolean expressions.
There are three commonly used boolean operators in Java: ! (not),
&& (and), and || (or).3 If p and q are both of type boolean, then
1. !p (not p) has the value true if and only if p has the value false.
2. p && q (p and q) has the value true if and only if both p and q have
the value true.
3. p || q (p or q) has the value true if and only if at least one of p or
q has the value true.
The results of applying the boolean operators can be summarized in truth
tables as shown below.
p q !p p && q p || q
true true false true true
true false false false true
false true true false true
false false true false false
3 Appendix G discusses other, less commonly used, boolean operators.
3.4. BOOLEAN OPERATORS 107

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

These operators can be mixed in expressions with other operators.


The precedence order for the operators that we have seen up to now is

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

In evaluating expressions involving && or ||, Java uses a technique


known as lazy evaluation or short circuit evaluation or conditional evalua-
tion. To show how this works, consider the expression p && q. Evaluating
this from left to right, if p is false, then the entire expression must be false,
no matter what value q has. Since the value of the expression is known
without evaluating q, Java does not even look at q. Of course, if p is true,
then Java must examine q to determine the value of the entire expression.
A similar situation arises with expressions of the form p || q. Here,
if p is true, then the entire expression must be true so Java can again be
lazy and not bother looking at q.

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.

Boolean expressions can often be simplified using a pair of rules known


as de Morgan’s laws, in honour of the logician Augustus de Morgan. To
illustrate one of these laws, consider the statement “This book is boring
and this course is useless.” For this statement to be true, both parts of it
must be true. For the statement to be false, one or both of its parts must
3.4. BOOLEAN OPERATORS 109

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)

2. Simplify as much as possible.


(a) !(a <= b) (b) !(x != 0)
110 CHAPTER 3. DECISION MAKING

(c) x < 5 && x < 8 (d) a != 0 || a != 2


(e) a > b || a == b (f) x > 0 && x <= -2

3. Write a boolean expression that will be true if and only if the int
variable i satisfies the condition 0 ≤ i ≤ 5.

4. Use de Morgan’s laws and the fact that !!p is equivalent to p to


simplify each expression.
(a) !(p && !q) (b) !(!p && !q)
(c) (!(!p || !q)) (d) !(x != 0 && !(y == 0))

5. In addition to de Morgan’s laws, there are a number of other laws


that can be used to simplify boolean expressions. A useful list of
equivalences is given in the following table. (The symbol ≡ is used
to mean that two boolean expressions are equivalent.)

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

Use these relationships to simplify each of the following expressions


as much as possible.
(a) (p && q) || (p && !q) (b) !p || (p && q)
(c) !p && (!q || q) (d) p || !(p && q)
(e) p && !p && !q (f) (p && !q) || p
(g) !(!p && q) && (p || q) (h) !(p || !q) || (!p && !q)
3.5. NESTED IF STATEMENTS 111

6. The exclusive or operator, which is sometimes written as ⊕, has the


following truth table.

p q p⊕q
true true false
true false true
false true true
false false false

Write a Java expression that is true if and only if p ⊕ q is true.

3.5 Nested if Statements

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.

In the next example nesting is used in a slightly more complex way.

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

In the examples that we have shown so far, we have been following


the same indentation pattern that we introduced at the beginning of this
chapter, aligning corresponding if’s and else’s and indenting two spaces
past them for statements nested inside the if. If we have a long sequence
of nested if statements, the resulting indentation can become excessive,
leaving us little room on a line for our statements. To overcome this, we
sometimes vary our indentation pattern.

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 problem arises with nested if statements when one of the if state-


ments has no else clause. The following segments illustrate the problem.

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");

The indentation in segment A indicates that the else clause is associ-


ated with the second if. The indentation in segment B indicates that the
else clause is associated with the first if. Since indentation is ignored by
the computer, the two segments have the same structure. The problem of
deciding which interpretation is correct is called the dangling else problem.
In Java, interpretation A is the correct one. The general rule is to associate
an else with the nearest available preceding if.
If we want to have the else associated with the first if, the preferred
solution is to use brace brackets to “close off” the second if as shown in
the next segment.

B*: if (p)
{
if (q)
System.out.println("Path 1");
}
else
System.out.println("Path 2");

An empty statement provides an alternative solution to the problem.

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

if (temperature > maxTemp)


System.out.println("Porridge too hot");
if (temperature < minTemp)
System.out.println("Porridge too cold");
if (temperature >= minTemp && temperature <= maxTemp)
System.out.println("Porridge just right!");

2. Consider the following statement.

if (age < minAge)


if (income > minIncome)
System.out.println("Accept");
else
System.out.println("Reject");

What will the statement print if

(a) age > minAge and income < minIncome?


(b) age < minAge and income < minIncome?

3. Using nested if statements, write a single statement that prints the


smallest value contained in the variables a, b, and c.

4. Write a statement that, if the value of the variable item is negative,


adds its value to negativeSum but, if it is positive, adds its value to
positiveSum. If, on the other hand, the value of item is zero, the
statement should simply increase the variable zeroCount by one.

5. Assume that the following declarations have been made.


int year;
boolean isLeapYear;
Write a fragment that will assign isLeapYear the value true if year
represents a leap year and false otherwise.
A year is usually a leap year if its number is a multiple of four. It is
not a leap year, however, if its number is a multiple of 100 but not
a multiple of 400. (As examples, 1900 was not a leap year but 2000
was a leap year.)
116 CHAPTER 3. DECISION MAKING

3.6 Choosing From Many Alternatives

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;
}

In executing this statement, Java first determines the value of operator.


Then, based on this value, it starts to execute the code inside the brace
brackets, starting at the point indicated by the appropriate case. The
break; statements cause Java to stop executing statements inside the
switch and to jump down to the point just after the switch. For ex-
ample, if operand1 = 3, operand2 = 4, and operator = ’*’, Java would
print the value of 3 * 4 and then exit the switch.

In the example, the final break; is not necessary but it is a good


programming practice to put it there to make sure that the switch will
3.6. CHOOSING FROM MANY ALTERNATIVES 117

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;
}

In any switch statement, the expression that controls it must have


some integer value (byte, short, int, long, or char). This expression is
usually simply a variable but it can be an arbitrarily complex expression;
the case values, on the other hand, must be constants. The order of the
cases is arbitrary. If there is no default case and the value of the expression
does not match any of the cases, then the statement does nothing (and Java
gives no error message).
118 CHAPTER 3. DECISION MAKING

Exercises 3.6
1. Rewrite the following using the indentation style shown in the text.

switch(month){case 1:case 3:case 5:case 7:case 8:case 10


:case 12:length=31;break;case 4:case 6:case 9:case 11:
length=30;break;case 2:if(isLeapYear)length=29;else
length=28;break;}

2. (a) In Example 1, what would the fragment print if operand1 = 8,


operand2 = 2, and operator = ’-’?
(b) What would be printed with these values if there were no break;
statements in the switch statement?
3. A sequence of six tests, all scored out of 100, are to be given different
weightings in determining a final mark. Write a program fragment
that computes the appropriate weighted score for one test. The frag-
ment should first read values of testNumber and score. Using a
switch statement, it should then compute and print the appropriate
value of weightedScore using the weightings given in the following
table.

Test Number Weight


1 10%
2 20%
3 20%
4 15%
5 15%
6 20%

For example, input of


3
27
should produce output of
A score of 27 on test 3 gives a weighted score of 5.4

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

The last digit of 547 is seven.

5. Write a program that reads a date in numeric form as a year followed


by a month followed by a day and then prints the date as one might
on a cheque. For example, input of
2000
7
1
should produce output of

July 1, 2000

3.7 Avoiding Errors and Debugging

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|).

2. It is legal to embed an assignment inside a condition. As an example,


an if statement could begin with
if ((x = y + z) > 0) ...
Here the expression (x = y + z) > 0 first assigns to x the value of
120 CHAPTER 3. DECISION MAKING

y + z and then tests the result to see if it is positive. We some-


times refer to the assignment as a side-effect of the evaluation of
the condition. Side-effects in programming, like side-effects in taking
pharmaceutical drugs, are usually undesirable. Generally speaking,
we recommend that expressions involving side-effects be avoided. For
the given example, if you later choose to eliminate the if statement,
you may inadvertently also eliminate the assignment. Usually it is
best to write code that may be a little longer than is necessary in
order to make your intentions clear. The example here could be re-
placed by
x = y + z;
if (x > 0) ...

3. Be very careful about your use of semi-colons. One extra semi-colon


in the wrong place can completely destroy the logic of a program.
For example, the fragment

if (x < 0);
System.out.println("Value is negative");

will always print the message “Value is negative”, no matter what


value x actually has. The problem here is the semi-colon at the end
of the first line. Using our indentation scheme, Java interprets the
fragment as

if (x < 0)
;
System.out.println("Value is negative");

The extra semi-colon is considered to be an empty statement which


is executed if x is negative. This terminates the if statement. No
matter what value x has, the statement that prints the message is
executed.

4. For statements to be treated as a block, they must be enclosed in


brace brackets. Consider the following example.

if (value < 0)
System.out.println("Negative value made positive");
value = -value;
3.7. AVOIDING ERRORS AND DEBUGGING 121

The indentation indicates that both statements should be executed if


the condition is true but the compiler ignores indentation. Without
brace brackets surrounding the two indented statements, the second
one is not part of the if statement. The fragment will make nega-
tive values positive but it will also cause positive values to be made
negative.

Debugging

1. The most common error committed by beginning Java programmers


using if statements is to write = when == is required. The error will
usually be detected as a syntax error by the compiler. The error mes-
sage that it produces is (usually): incompatible types. To explain
this, consider the statement that begins with
if (n = 5) ...
where n is of type int. Inside the parentheses is an assignment, an
expression whose type is that of the variable on the left side of the
assignment. In the example shown here, the type of the assignment
expression is int because n is of type int. Java, of course, is expect-
ing a boolean expression (like n == 5). Since the type that it finds
is not what it wants, it complains.
2. Another frequent error with if statements is the failure to take some
action for every possible eventuality. The Java compiler is sometimes
clever enough to spot this sort of problem so that a fragment such as

int m;
int n = In.getInt();
if (n > 0)
m = 1;
else if (n == 0)
m = 2;
System.out.println(m);

will, during compilation, produce the error message “variable m


may not have been initialized”. The problem here is that m will
not be given a value if n < 0. Unfortunately, sometimes the com-
piler is a bit over-zealous in flagging possible errors of this type. For
example, the fragment
122 CHAPTER 3. DECISION MAKING

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).

3. In mathematics, we often restrict the range of a variable x by writ-


ing expressions like a < x ≤ b. Writing similar expressions in Java
causes an error. To see what is wrong with such expressions, suppose
that we were to try to evaluate a < x <= b as Java might do. Work-
ing from left to right, we would begin by evaluating a < x giving a
boolean result (true or false). When we then attempted to com-
plete the evaluation, we would be left trying to compare a boolean
value with b, producing an error. Java’s compiler is smart enough to
spot such problems, producing an error message during compilation.
The solution to the problem is to rewrite the expression in the form
a < x && x <= b.

4. Another mathematical shortcut that produces an error in Java is an


expression of the form x, y > 0. Trying to write this in Java either as
it is or in the form x && y > 0 is a mistake. To evaluate x && y > 0,
Java would first have to evaluate the sub-expression y > 0 (because >
has higher precedence than &&) but then the expression would reduce
to either x && true or x && false. For x having a numerical value,
either of these expressions would be nonsense in Java. Again, the
compiler will catch such an error and refuse to compile a program
containing it. The expression should be replaced by x > 0 && y > 0.

5. If a program that contains if or switch statements is running but


producing incorrect results, the error may be caused by the program
executing incorrect statements because the logic is wrong. To deter-
mine which path a program is actually taking, insert tracing state-
ments in every possible branch of the program. For example, to trace
a program containing a segment of the form
3.7. AVOIDING ERRORS AND DEBUGGING 123

if ( ... )
if ( ... )
<statement1 >
else
<statement2 >
else
<statement3 >

tracing statements should be inserted at each of the points at which


<statement1 >, <statement2 >, and <statement3 > are shown.
6. Tracing statements used for debugging can be turned on and off easily
using if statements. To do so, create a boolean variable DEBUG that
is set to true when debugging and false otherwise. Then place all
tracing statements inside if statements of the form
if (DEBUG) ...

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

(d) x is greater than both a and b


3. Examine the following fragment and then answer the questions that
follow it.

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.

4. Suppose that a program contains the following statement.

if (a > 0)
if (b < 0)
System.out.println(1);
else
System.out.println(2);
else
System.out.println(3);

Under what circumstances will the statement print


(a) 1? (b) 2? (c) 3?

3.8 Review Exercises 3

1. Assume that the following declarations and assignments have been


made:

int age = 16;


int height = 175;
int weight = 70;
char sex = ’M’;
boolean healthy = true;
3.8. REVIEW EXERCISES 3 125

Using these values, evaluate each expression.

(a) age >= 16 && healthy


(b) !(weight <= 75) && height >= 180
(c) age > 10 || sex == ’F’ && height < 170
(d) !(height < 160) && (weight < 60)

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("The client preferred ")


if (flavour == ’B’)
System.out.print("bar-b-que");
else if (flavour == ’V’)
System.out.print("vinegar");
else
System.out.print("other");
if (style == ’C’)
126 CHAPTER 3. DECISION MAKING

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;

8. Rewrite using nesting.

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");

9. Consider the following statement.


3.8. REVIEW EXERCISES 3 127

if ((a <= b && b <= c) || (a >= b && b >= c))


System.out.println("Values in order");
else
System.out.println("Values out of order");

(a) Describe in a few words the effect of the statement.


(b) Write a statement that has the same effect but uses neither &&
nor ||.

10. In the country of Rahmania, the cost of mailing a letter is 40 sinas


for letters up to 30 g, 55 sinas for letters over 30 g and up to 50 g,
70 sinas for letters over 50 g and up to 100 g, and then an additional
25 sinas for each additional 50 g or part thereof. Write a program
that prompts the user for a mass and then gives the cost of mailing
a letter having that mass.

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

Write expressions that will be true if and only if


128 CHAPTER 3. DECISION MAKING

(a) s1 is longer than s2 .


(b) s1 is entirely contained within s2 .
(c) s1 and s2 have no points in common.
(d) s1 and s2 overlap partially (but not completely).

Projects

13. Write a program to compute an employee’s weekly pay and produce


a pay slip showing name, gross pay, deductions, and net pay. The
program should first prompt the user for:

(a) family name


(b) given name
(c) hourly rate of pay
(d) number of hours worked that week (Any hours over 40 are paid
at double the normal hourly rate.)
(e) a letter indicating the employee’s tax category
A: no tax deduction
B: tax is 10% of gross pay
C: tax is 20% of gross pay
D: tax is 29% of gross pay
E: tax is 35% of gross pay
(f) either a Y or an N to indicate whether or not the employee
wants $20 deducted from the weekly pay as a contribution to
the United Way Charity

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.

15. The government of Simpleton has devised what it thinks is an easy


income tax system, but its citizens still need help. They have commis-
sioned you to write a program to ask a citizen a few simple questions
and compute the tax that is payable or the refund that is due. You
must write your program as clearly as possible so that the govern-
ment can verify it easily. Your program should first ask a citizen
for his/her income (income), housing cost (houseCost), number of
children (totalChildren), and number of children that are in school
(schoolChildren). It should then compute and print the tax payable
or the refund due. The tax rules are as follows. The Simpleton tax
rate is 18% but citizens are not taxed on the first $10 000 of income
unless they pay more than $8 000 for housing. For every child, a
Simpleton citizen gets a $500 tax reduction, or $1 000 if the child
is in school. This reduction never results in citizens getting refunds
unless their housing costs are less than $6 000 and they have more
than two children, at least one of whom is in school. Finally, if the
tax payable is more than $2 000, then it is increased by an additional
15% surtax.
Real Jobs — Real People
Name: Lance Hooper
Title: President
Company: Policom Canada Inc.
Education: B.Sc., University of Guelph (1995)

Q: How did you get started with computers?


A: My degree from the University of Guelph is actually in agriculture but,
well before I graduated, I realized that I was more interested in computers
than agriculture and I took several courses in computer science along with
my other courses.
Q: What did you do after graduation?
A: I started out freelancing, creating web pages for small companies and trying
to pick up the skills I needed as I went along. That was a very exciting
time as the internet was just starting to go towards a graphical format with
the first browsers like Mosaic 1.0.
Q: Why did you leave that and what did you do next?
A: I got a contract to do some work for TD Bank and that led to a full-time
job with them, as their webmaster. I worked for them for about three
years developing interactive services at TD Bank Financial Group. It was
stimulating because at that time TD Bank was initiating a wide variety of
web services, ahead of their competition. I eventually had a team of about
a dozen people working for me and we developed over 25 000 pages of
public web content spanning fifteen domains and three languages (English,
French, and Mandarin).
Q: Why did you not stay with TD Bank?
A: They merged with Canada Trust and for a long period almost all of the
energy of everybody at the bank was focused on making the merger work
so the new initiatives that had made the job exciting were put on hold.
Even after the merger, I found the bank to be very slow to make decisions
about innovations.
Q: So then you moved on to CertaPay?
A: Yes, I and a friend of mine, along with a couple of guys who had some money,
were partners in a firm called CertaPay, an internet payment gateway. We
created the first system in the world that, among other things, allows people
to send money directly (and securely) from their bank account to anybody

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 our work up to this point, you may have developed


the feeling that writing instructions to a computer re-
quires more work than you would need to do it by hand.
Now, however, we start to look at instructions that make
the computer a very powerful tool. Specifically, we look
at ways of constructing loops. These are structures that
allow any number of statements to be performed repeat-
edly. Java has three statements that enable us to create
loops: while, do, and for. We first examine each of
them individually and then compare them to see which
one should be used in a particular situation. Finally,
we examine situations in which it is useful to use some
combination of loops to solve a problem.
134 CHAPTER 4. REPETITION

4.1 while Statements

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

A flow chart for a general while statement can be used to illustrate


this process.

H ?
 H
 H Execute
 Evaluate HH true - <statement>
HH <expression> 
H  once
H 
H
false
?

The next example shows the use of a while statement in a complete


program.

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.

1. The statement int value = In.getInt(); that precedes the while


statement is necessary to initialize the variable value before the loop
is entered for the first time. Without this initialization, value would
be undefined the first time that the while statement was encountered
and an error would occur. This pattern of initialization followed by
a loop is one that we will see many times.

2. By using a compound statement with brace brackets, we can have


more than one statement in the body of the loop, exactly as we did
with if statements.

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.

6. If a statement in a while loop changes the boolean expression that


controls the loop to false, the program does not immediately proceed
to the statement following the loop. Once a program has entered
the body of a while loop, the entire sequence of statements in the
body of the loop is executed and only then is the boolean expression
re-evaluated to see if the loop should be entered again.
4.1. WHILE STATEMENTS 137

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. */

System.out.println("Submit marks (value < 0 to stop");


int totalMarks = 0;
int numberOfMarks = 0;
int nextMark = In.getInt();
while (nextMark >= 0)
{
totalMarks += nextMark;
numberOfMarks++;
System.out.println("Next mark:");
nextMark = In.getInt();
}
if (numberOfMarks > 0)
{
int average =
Math.round((float)totalMarks/numberOfMarks);
System.out.println("Average for " + numberOfMarks
+ " students is " + average);
}
}
}
138 CHAPTER 4. REPETITION

In this program, the initialization includes both setting the variables


totalMarks and numberOfMarks to zero as well as reading the initial value
of nextMark. As in the previous example, the statement
nextMark = In.getInt();
is repeated inside the loop to read subsequent values. In this way, the pro-
gram will eventually read a sentinel value (here, any negative number) and
conclude by finding the average. Notice also that the program only finds
the average if there has been at least one mark entered; if only a sentinel
was supplied, the program does not attempt to calculate an average.

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

Since a do statement executes the statement in its body before evalu-


ating the boolean expression that controls the loop, it is useful in situations
where we know that we want to perform the loop at least once. Reading
input and repeatedly checking to see that it is valid is a common example
of this kind of situation, as the next example illustrates.

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

System.out.println("How many items to add?");


do
{
4.3. SIMPLE FOR STATEMENTS 141

howMany = In.getInt();
if (howMany <= 0)
System.out.println("Give a positive value.");
}
while (howMany <= 0);

3. Write a fragment that forces a user to supply either y or n in response


to the question "Continue? Respond with y or n".

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.

4.3 Simple for Statements

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

expression (which must be of type boolean) is a condition that controls


the loop, and the third expression is a modifier that is evaluated after each
execution of the body of the loop. The next example shows how an actual
Java for statement works. The example uses a for loop to find the value
of the sum of a series.

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;

This for statement first initializes i to 1. Then it repeatedly checks that


i ≤ 100 and, if it is, it increments sum by i*i and increments i by 1.

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
?

Often, for statements are controlled by counters (as we saw in Ex-


ample 1 where the variable i controlled the loop). Since we often need
such loop control variables, Java allows us to declare them right in the for
statement as the next example shows.

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));

Variables declared in the initialization part of a for statement are only


usable inside that statement. Thus, in Example 2, the variable x is only
usable inside the for. If we added the statement
System.out.println(x);
after the for, we would get an error message because a variable declared
within a statement is only known inside that statement.

Exercises 4.3
1. What does each statement print?

(a) for (int i = -3; i <= 3; i++)


System.out.print("*");
144 CHAPTER 4. REPETITION

(b) for (int countDown = 5; countDown > 0; countDown--)


System.out.println(countDown + " seconds");
(c) for (char letter = ’P’; letter <= ’S’; letter++)
System.out.println("Give me a " + letter);
(d) for (int i = 2; i < 100; i *= i)
System.out.println(i);
2. Write statements that will print a table of values of the function
f(x) = 2x + 5 for the indicated values of x.
(a) x = 6, 5, 4, . . ., 0 (b) x = 0, 3, 6, . . . , 30
(c) x = −15, −10, −5, . . . , 15 (d) x = 1, 2, 4, 8, . . ., 1024

3. Write a fragment that uses a for statement to perform the indicated


action.
(a) Set the double variable sum to the value of
1 1 1 1
+ + +···+
1 2 3 1000
(b) Set the double variable sum to the value of
√ √ √ √
100 + 200 + 300 + · · · + 5000

(c) Set the long variable product to the value of

1 × 2 × 3 × · · · × 20

(d) Set the int variable total to the value of

(−12)3 + (−11)3 + (−10)3 + · · · + (20)3

(e) Set the double variable sum to the value of


√ √
3

25
1 + 2 + 3 + · · · + 25

4. Suppose that a large piece of paper with an area of 1.0 m2 and a


thickness of 0.090 mm is cut in half and the two pieces are stacked,
one on top of the other. Suppose further that the process of cutting
in half and stacking is repeated over and over again. Write a program
to find both the thickness of the pile and the area of each piece after
the procedure has been carried out forty times.
4.4. VARIATIONS ON FOR STATEMENTS 145

5. Write a program that reads a positive integer n and then prints an


“n-times table” containing values up to n × n. For example, if the
program reads the value 5, it should print

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.

4.4 Variations on for Statements


Although most uses of for statements have the simple form that we saw
in the last section, many variations are possible. One of the ways in which
we can extend the statement is by using commas to combine multiple ex-
pressions into larger ones as the next example illustrates.

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

There is no limit to the number of expressions that can be included


in the first and third parts of a for statement. If we want more than two,
we can simply add others using more commas. We cannot do this with the
second expression; it must be a single boolean expression.
If we carry the process of placing multiple expressions in the header of
the for to its extreme, we can sometimes produce a loop that has nothing
but an empty statement in its body. Such a situation is shown in the next
example.

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.

Instead of increasing the number of expressions in the first and third


parts of a for statement, we can eliminate either or both of them completely
if there is no need for explicit initialization or modification.

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) for (i = 1, j = 12; i < j; i++, j -= 2)


System.out.println(i + " " + j);
(b) for (i = 1,product = 1; i <= 5; product *= i,i += 2)
System.out.println(product);
(c) for (n=5,i=1; n>1; System.out.println(i),n--,i*=2)
;

2. Rewrite each fragment as a single for statement whose body consists


of only an empty statement (as illustrated in Example 2).

(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.

(a) Set the double variable sum to the value of

1 2 3 19
+ + +···+
2 3 4 20

(b) Set the int variable total to the value of

1 × 100 + 2 × 98 + 3 × 96 + · · · + 50 × 2
148 CHAPTER 4. REPETITION

4.5 Comparing Loop Structures


With three loop structures available in Java, how do we decide which to
use in a given situation? To some extent, this is a matter of personal taste,
but usually one of the statements is more appropriate than either of the
others.
The for statement should usually be used if we know, before we start
the loop, how many repetitions we want. Problems that call for the reading
of a given number of data items or the production of a table of a given size
are examples of situations where we would normally use a for statement.
The while and do statements should normally be used when the num-
ber of repetitions is not known before we start execution of the loop. If it
is convenient to test the condition that controls the loop before entry to
the loop body, then a while is the usual choice. If the condition is only
established within the body, then a do should be used. If it is possible that
the loop body may never be entered, a while statement should be used; if
it is certain that the loop body will be executed at least once, then a do
might be the better choice.

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.

(a) for (i = 0; i < 10; i++)


{
x = 2 * i - 10;
y = Math.abs(x / 5 - 2);
System.out.println(x + " " + y);
}
(b) product = 1;
do
{
System.out.println("Next value please");
next = In.getInt();
if (next != TERMINATOR)
product *= next;
}
while (next != TERMINATOR);
4.5. COMPARING LOOP STRUCTURES 151

3. Describe what the fragment does and then rewrite it using a do state-
ment.

(a) System.out.println("Enter a value - zero to stop");


value = Math.abs(In.getInt());
biggest = value;
while (value != 0)
{
if (value > biggest)
biggest = value;
System.out.println("Enter a value (0 to stop)");
value = Math.abs(In.getInt());
}

(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

4.6 Nesting Loop Structures

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

digitSum += absN % 10; // add last digit


absN /= 10; // and delete it
}
System.out.println("Sum of digits of " + n
+ " is " + digitSum);
}
}
while (n != 0);
}
}

Exercises 4.6
1. Assuming that SIZE has the value 5, determine the output that will
be produced by the following fragment.

for (i = 1; i <= SIZE; i++)


{
for (j = SIZE; j >= i; j--)
System.out.print("*");
System.out.println();
}

2. Write a program that will print a triangular pattern of asterisks whose


maximum width is determined by input from the user. The diagram
shows the pattern that should be printed if the user’s input is 5.
*
**
***
****
*****
****
***
**
*
4.6. NESTING LOOP STRUCTURES 155

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

4.7 Avoiding Errors and Debugging

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.

If a loop has both counting and conditional elements, it is usually


(but not always) best to use a for statement.

2. Enter a loop appropriately. Be sure that you have correctly initialized


all variables that need to be defined before you enter the loop.

3. In a conditional loop, be sure that there is some action that ensures


that the program will eventually exit from the loop.

4. Be careful, as usual, of having semi-colons where they do not belong.


As an example, consider the following fragment.

for (int i = 1; i <= 100; i++);


System.out.println(i + " " + i*i);

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.

6. If you write a for statement that starts with the header


for (int i = 1; i <= 100; i++)
then anybody reading your program expects that the loop will be
executed 100 times. If the loop contains a statement of the form
if (x < 0)
i = 101;
then the loop may not be executed 100 times. The header of your
loop is, in this case, misleading to a reader of your program. If you
were to write something like
for (int i = 1; i <= 100 && x >= 0; i++)
then your intentions would be much clearer and less likely to cause
an error.

7. We saw in Section 4.4 that in for statements, the expressions con-


tained in parentheses (following the word for) can take many forms.
Although there are occasions when these constructs are useful, they
can be confusing and should, in general, be avoided. It is almost
always better to keep things simple and clear.

Debugging

1. If a program containing a loop compiles correctly but, when it is


run, the computer appears to do nothing, then you may have written
an infinite loop. Once you have stopped the program, try running it
again with tracing statements in the body of the loop. If the program
is caught in an infinite loop, any output in the body of the loop will
fly by on the screen. To make the tracing statements readable in such
circumstances, insert a statement like
char junk = In.getChar();
after your tracing statement. This will give you a chance to read the
output of the tracing statement each time the program executes the
body of the loop.

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

program could be something as simple as the difference between the


expressions while (x > 0) and while (x >= 0).
3. If a program containing a conditional loop is not performing correctly
and the expression that controls the loop is a compound expression,
check that all and’s, or’s, and not’s are being used correctly.
4. Using floating point values to control a loop can lead to problems
because of roundoff errors. As an example, consider the statement

for (double x = 0; x < 1; x += 0.1)


System.out.println(x);

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.

for (int i = 0; i < 10; i++)


System.out.println(i/10.0);

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;
}

5. Rewrite each loop to make it clearer.


(a) for (int i = 1; i <= 20; i++)
if (i % 2 == 0)
System.out.println(i + " " + i*i);
(b) for (int i = 0; i <= 10; i++)
if (i*i < 2*i + 4)
System.out.println(i + " " + i*i);
else
i = 11;
(c) int i = 20;
while (i > 0)
{
System.out.println(i + " " + i*i);
i--;
}
160 CHAPTER 4. REPETITION

4.8 Review Exercises 4

1. What will be printed by each fragment?


(a) for (i = 0; i <= 5; i++)
System.out.println(i*i);
(b) n = 3;
do
System.out.println(++n);
while (n < 8);
(c) for (i = 25; i > 0; i -= 4)
System.out.println(i);
(d) for(i = j = 1; i < 5; i++)
{
j *= i;
System.out.println(j);
}
(e) n = 26;
while (n != 1)
{
if (n % 2 == 0)
n /= 2;
else
n = 3 * n + 1;
System.out.println(n);
}
2. Describe in a few words what would be stored in the variable value
after execution of each fragment.
(a) for (i = value = 0; i <= 10; i++)
value += i;
(b) n = In.getInt();
value = 0;
while (n % 2 == 0)
{
4.8. REVIEW EXERCISES 4 161

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

(b) What guessing strategy will minimize the expected number of


incorrect guesses?
8. Let n be a positive integer consisting of the digits dk . . . d1 d0 . Write a
program that reads a value of n and then prints its digits in a column,
starting with d0 . For example, if n = 3467, then the program should
print
7
6
4
3
9. Rewrite the program in the previous question so that it prints the
digits of n in the opposite order, starting with dk and working down
to d0 .
10. Write a program that takes as input the number of days in a month
and the day of the week on which the first of the month occurs. The
program should produce a display of the calendar for that month in
standard form. For example, if there are 30 days in the month and
the first day of the month is Wednesday, the input would be 30 and
Wednesday, while the output should be similar to the following.

Sun Mon Tue Wed Thu Fri Sat

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

of time. The program should prompt the user to provide an interest


rate as a percent (from 0 to 20) and a number of compounding peri-
ods (from 0 to 50). The program should then produce a table with
appropriate headings showing the amount to which $1 per period
(paid at the start of each period) has grown for 1, 2, 3, . . . , n periods
where n is the value provided by the user. The program should reject
invalid input, continuing to prompt the user until valid data have
been provided. (Do not attempt to screen for non-numerical input.)
Amounts in the table should be rounded to the nearest cent.
12. Write a program that first prompts the user for a value of x, reads x
(as a double) and then computes and prints the value of ex without
using either of the methods exp or pow from the Math class. The
value of ex is given by the following power series.
x2 x3
ex = 1 + x + + +···
2! 3!
In computing the value of ex , your program should continue to add
terms of the power series until it reaches a term whose absolute value
is less than 10−15 times the absolute value of the sum of the previous
terms.
The symbol n! is read as n factorial. If n is a positive integer, then

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

As an example, input of 48070 should produce output like the fol-


lowing:
48070
4807
473
44
0
48070 is divisible by 11

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

roots. It should also be able to give appropriate results if one or


more of the coefficients are equal to zero. The program should be
interactive. The form of the exchange between a user and a computer
is shown in the following example.

Do you want to solve an equation (y/n)? y


Enter a: 1
Enter b: -5
Enter c: 6
Root1: 2.0
Root2: 3.0

Do you want to solve an equation (y/n)? y


Enter a: 1
Enter b: -1
Enter c: 4.25
Root1: real part: 0.5 imaginary part: -2.0
Root2: real part: 0.5 imaginary part: 2.0

Do you want to solve an equation (y/n)? y


Enter a: 0
Enter b: 2.4
Enter c: 9.0
Root1: -3.75

Do you want to solve an equation (y/n)? n

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

Here is an example, determining the product 57 × 86 = 4902:

/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

As we proceed in our study of programming, the prob-


lems that we meet become more complex. As the com-
plexity of a task increases, it becomes more and more
difficult to see the entire solution at once. In such a
situation, it is useful to break the problem up into man-
ageable subproblems, solve each of these, and then put
the pieces together to get the solution to the full prob-
lem. Writing programs in this way is often called mod-
ular programming because it decomposes the program
into modules. In this chapter, we examine one of Java’s
most important structures for producing modularity —
the method.
170 CHAPTER 5. METHODS

5.1 Basics

Modular programming is useful for a number of reasons. Some of these


reasons are:
• Modular programming allows us to think about one part of a large
problem more or less in isolation from the rest.
• Many people can work at various parts of the problem at the same
time.
• If some parts of a program are identical (or nearly so), we may be able
to use the same module repeatedly in various parts of the program.
• We may be able to write a module once and then use it in many
different programs.

• By carefully testing each module in isolation, we may develop some


confidence that the entire program will work when we put the modules
together.
Java has a number of features that allow us to introduce modularity.
The most basic of these is the use of methods. The idea of a method is
not new to us. Every program that we have seen contained the method
called main. We have also seen many examples of programs that use other
methods: our very first program used the method println and we have
encountered many others since then: the methods of the class In for reading
data, the methods of the class Math that we studied in Chapter 2, and the
methods equals and compareTo of the class String. In this chapter, we
are going to see how to create our own methods (other than main) as tools
for solving our own problems.
To get started, suppose that we have been asked to handle the ac-
counting for a local video rental store. The program’s output will consist
of a number of pages, each of which should have a heading of the form
Ahmed’s Video Wonderland
70 Roehampton Avenue
555-1212
Rather than write all the statements that would be needed to print
this at each of the required points in the program, it would be more efficient
if we could simply give a single command to print a heading whenever it
5.1. BASICS 171

is needed. Since there is no predefined method in Java for performing this


task, we must create one. The next example shows how to do so.

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.

public static void printHeading ()


{
System.out.println("\fAhmed’s Video Wonderland");
System.out.println(" 70 Roehampton Avenue");
System.out.println(" 555-1212");
}

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
.
.
}

public static void main (String[] args)


{
.
.
printHeading();
.
.
printHeading();
.
.
printHeading();
.
.
}
}

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

brace bracket. If we want to terminate execution of the method before this,


we can do so by using a return statement.

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));
}

Although the use of return statements can be useful, it is often best


(and always possible) to avoid them and have the method simply “fall
through” to its end, at which point a return occurs automatically.

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

public static void main (String[] args)


{
System.out.println("Old MacDonald had a farm"); //4
printChorus(); //5
System.out.println("And on that farm he had "
+ "a pig"); //6
printChorus(); //7
}
}

3. Rewrite the method of Example 3 so that it avoids the use of a return


statement.
4. Write a method that will simulate the results of rolling two fair dice
by printing two random integer values in the range 1 to 6 along with
their total. Sample output from a call to the method could be:
4 and 3 - a total of 7

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();
}

In the definition of the method printRow in Example 1, the specification


(char c, int n) is called a parameter list. It serves as a declaration of
the variables c and n as the parameters of the method.
To use this method to write a line containing ten asterisks, we could
write the call in the form printRow(’*’,10); The values ’*’ and 10 are
called the arguments of the method.1
When a method like printRow is called, the values of the arguments are
automatically assigned to the method’s parameters. The order in which the
parameters are written determines which argument gets assigned to which
parameter. We refer to this automatic transfer of values from arguments
to parameters as a passing of values.
Arguments are not restricted to being constants as we have shown so
far. They can be expressions or variables.

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

2. These values would be automatically assigned to the parameters c


and n in the method printRow.
Method printRow
c n
’D’ 8

3. Using these parameter values, the method would then be executed,


producing the output line
Method printRow
c n
’D’ 8

DDDDDDDD

4. Control would then be passed back to the statement following the


call to printRow.
Point of Call
m
6

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

2. This value is then assigned to the parameter n in the method sample.


The parameter variable n is completely distinct from the other vari-
able n.
Method sample
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

4. Once execution of the method has concluded, control returns to the


statement after the call. The value of n here is unchanged.
Point of Call
n
3

Since argument values are being assigned to parameters, the types of


arguments and parameters must be appropriate. Usually, this means that
arguments and parameters are of the same type but this is not necessarily
the case. Any matching in which an assignment of the argument type can
be made to the parameter type is acceptable.

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.

public static void swap (int m, int n)


{
int temp = m;
m = n;
n = temp;
}
5.3. METHODS THAT RETURN VALUES 179

He then tested the method with the following fragment:

i = 7;
j = 3;
swap(i,j);
System.out.println("i = " + i + " and j = " + j);

What did the fragment print? Explain.


2. For the method doThis whose header was given in Example 4, state
which of the following calls are invalid. Justify your answer in each
case.
(a) doThis(0.5,2); (b) doThis(3,4);
(c) doThis(2.0,1.5); (d) doThis(-2,8.0);
(e) doThis(’x’,5); (f) doThis(3L,4);

3. (a) Complete the definition of the method printRect so that it


prints a filled rectangular pattern, using the character c, that is
width characters wide and height characters high.
public static void printRect
(char c, int width, int height)
(b) Modify your method so that it prints an open rectangular pat-
tern with blanks in the interior (if there is one).

5.3 Methods that Return Values

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

public static double f (double x)


{
double y;
if (x < 0)
y = x * x * x;
else
y = x * x;
return y;
}

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.

2. The method contains the statement return y; rather than simply


having the word return. This indicates, as before, that the method
should terminate at this point. In addition, however, it should also
send the value of y back to the point at which the method was called.

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

public static double f (double x)


{
if (x < 0)
return x * x * x;
else
return x * x;
}
5.3. METHODS THAT RETURN VALUES 181

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.

1. The value of the argument of f is evaluated.


Point of Call
a b
3.0 a-1 ⇒ 2.0

2. This value is then assigned to the parameter x in the method f.


Method f
x y
2.0

3. The method f is then executed, assigning a value to y.


Method f
x y
2.0 4.0

4. When execution of the method is complete, the value 4.0 is returned


to the point of the call. The value returned by the method is assigned
to b.
Point of Call
a b
3.0 4.0
182 CHAPTER 5. METHODS

If a method returns a value of type <t>, then it can be called in any


context in which an expression of type <t> can be used. We have already
seen this in calling pre-defined methods like Math.sqrt. The principle also
applies to methods that we have written ourselves.

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.

public static int mystery (double a, double b)


{
int value = 0;
if (a < b)
value = -1;
if (a > b)
value = 1;
return value;
}

(a) What is the identifier of the method?


5.4. METHOD OVERLOADING 183

(b) What are its parameters?


(c) What type of value is returned by the method?
(d) What part of the definition forms the heading?
(e) Rewrite the method using a nested if structure.
(f) Rewrite the method using multiple return statements.

2. The following method definitions lack both punctuation and inden-


tation. Rewrite each definition correcting these defects and state, in
a few words, the purpose of each method.
(a) public static char first(char a char b){if(a<b)
return a else return b}
(b) public static double second(double a double b)
{double answer if(a<b)answer=a-b else answer=b-a
return answer}

3. Assuming that the method f has been defined as it was in Example


1, state, with reasons, which of the following fragments are invalid.

(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.

5.4 Method Overloading


We have already seen that the parameters of a method need not have the
same types as the corresponding arguments; if the parameters are assign-
ment compatible with the arguments, that is perfectly acceptable. Some-
times, however, it is desirable to have a method that will accept a much
broader variety of arguments: possibly of very different types, possibly of
184 CHAPTER 5. METHODS

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.

• Another form of the method might be used to simulate repeated rolls


of a standard die. It could have the header
public static void rollDie (int rolls)
where rolls is the number of rolls that should be simulated and
printed. We could call this form of the method by writing
rollDie(3);
to print three rolls of an ordinary die.

• A third form of the method might have the header


public static void rollDie (int rolls, int faces)
where rolls is the same as it was previously and faces is the number
of faces on the die. We could call this form of the method by writing
rollDie(2,12);
to print two rolls of a dodecahedral (twelve-sided) die.
5.4. METHOD OVERLOADING 185

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)

(a) The call doThat(2,3); would invoke the first method.

(b) The call doThat(2.0,3); would invoke the second method.

(c) The call doThat(2,3.0); would cause an error since no signature


match can be made with either version of doThat.

(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.

Sometimes multiple matches can occur, in which case the match is


ambiguous and an error will occur during compilation.
186 CHAPTER 5. METHODS

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’);

3. Complete the definitions of the methods whose headers are given in


Example 1. If rolls is less than one or faces is less than four, the
methods should produce an error message.
4. Write methods called average, each having two arguments, that will
return the average of those values. If the arguments are both integers,
the value returned should be an int. If either or both values are
floating point numbers, the value returned should be a double. As
examples,
average(10,15) should return 12
average(10.0,5.0) should return 7.5
5.5. METHODS THAT RETURN BOOLEAN VALUES 187

5.5 Methods that Return boolean Values


A particularly effective way of improving the clarity of programs is through
the use of boolean methods — queries that return either true or false.
Often the boolean expressions that control selection or repetition state-
ments are quite complex. Trying to read through such an expression can
cause a reader to get caught up in details and lose track of the flow of
the program. If a boolean method with some descriptive identifier is used
in place of such an expression, the resulting code should be much easier
to follow. An identifier of a boolean method usually begins with “is”,
indicating that the value returned by the method is the answer to some
true/false question.

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;
}

In Example 1, the value returned by the method is always identical to


the value of the expression n >= 13 && n <= 19. We can take advantage
of this to make the definition of the method more concise, as follows.
public static boolean isTeen (int n)
{
return n >= 13 && n <= 19;
}
188 CHAPTER 5. METHODS

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 == ’!’);

2. Write a boolean method isDivisible with two int parameters. The


method should return true if and only if the first parameter value is
exactly divisible by the second.

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.

System.out.println("Letter Grade? (y/n)");


char response = In.getChar();
System.out.println("Value of grade?");
if (response == ’y’)
{
char mark = In.getChar();
if (isPass(mark))
System.out.println("Pass");
5.6. SCOPE AND ACCESSIBILITY 189

}
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).

5.6 Scope and Accessibility

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

... // can use i, j, and k here


} // scopes of j and k end here
... // still in scope of i here
} // scope of i ends here

It may be useful to picture each block as a rectangle in which the


variables declared in a block are only known inside that block. For the
previous example, we might use a diagram like the following in which the
scope of an identifier extends from the point at which is declared to the
end of the box in which the declaration takes place.

sample

int i

int j

int k

The scope of a parameter is the entire method in which the parameter


is defined, so a parameter behaves like a variable declared at the very
start of a method. If a variable is declared inside a block and that block
terminates, the identifier can be re-used elsewhere. Java does not, however,
allow us to make scopes overlap. Thus, a variable cannot be declared inside
a block if it has been previously declared in a containing block.
5.6. SCOPE AND ACCESSIBILITY 191

Example 2
Consider the following outline of a method:

public static int sample (double a)


{
...
for (int i = 1; ...)
{
...
}
...
double i = 0;
...
}

We again illustrate the scopes with a diagram.

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

One consequence of the scope rules is that a variable declared in one


method cannot be seen in another method. As a result, we do not have
to worry about using the same identifiers in different methods. If we make
the declaration
int i = 0;
in one method and the declaration
int i = 1;
in another method, there is no confusion as each identifier is local to its
method.
Associated with the concept of scope of variables is that of accessibility
of methods. Up to this point, the definitions of all of our methods have
started with the word public. The word public is an example of an access
control modifier.
To start to examine the idea of accessibility, it is necessary to explore
(a bit) the way that large projects can be organized in Java. Initially, our
programs involved only a main method in a class. Now, in this chapter,
we have seen that a class can contain more than one method. Even larger
groupings are possible with a number of classes contained in a single pack-
age. A package is simply a collection of classes. Java itself is made up of
a number of packages such as java.applet for classes that can be used
to create programs that run inside a web browser and java.io for classes
that perform input and output. We can specify that a file belongs to a
particular package by placing a package statement as the first line of the
file. A package statement has the form

package <package identifier>;

If we do not specify that a class is to be placed in a package, it is placed


in the default package — the directory in which the program was created.
To say that a method is public means that it can be seen and called
from anywhere — from any method in any class in any package. The main
method must always use this modifier but other methods need not. If
we replace the word public by the word private in a method’s header,
then the method can only be accessed from within the class in which it is
defined. A third option is to omit the access control modifier completely.
In this case, the method can be accessed from within the package in which
it is defined. There are other possibilities but, for now, these three cases
are all that we need. We will, shortly, be using multiple classes but we
will never be creating more than one package. If all of our classes are
contained in one package, public access is equivalent to package access. In
5.6. SCOPE AND ACCESSIBILITY 193

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.

public static void showScope (int i, double x)


{ // 1
int j;
... // 2
for (int k = 0; .. )
{
... // 3
double y;
... // 4
}
... // 5
{
float f;
... // 6
}
double z;
... // 7
}

2. What is a block?

3. (a) What is the difference between defining a method as public and


defining it as private?
(b) What is the result of omitting both public and private from
the header of a method?

4. For which method is the public modifier always required?


194 CHAPTER 5. METHODS

5.7 Programming with Methods

One of the primary reasons for having methods in programming languages


is to make it easier to solve complex problems. Having discussed how to
create methods, we are now going to look at how we might use methods
to create a solution to problems larger than those we have examined pre-
viously.
To illustrate the use of methods in problem-solving, we will consider a
problem related to one proposed by the mathematician Christian Goldbach
in 1742, in a letter to Leonhard Euler. In the letter, he made the suggestion
that has since come to be known as Goldbach’s Conjecture. It states that
every even number greater than two can be expressed as the sum of two
prime numbers2 . Despite the work of many number theorists over the years
and even the posting of a $1 000 000 prize for a proof, the conjecture is
still unproven (at the time that this was written). Although it has been
impossible to prove that the conjecture is true for every even number, it is
not too difficult to check the conjecture for a given even number and that
is the problem that we are going to examine here.
Specifically, we want to develop a program that will repeatedly prompt
the user for positive even integer values greater than two and attempt to
write each of those numbers as the sum of two primes. The program should
continue to read values until the user supplies input of zero. Invalid input
should be rejected gracefully.
To begin, let us try to develop a strategy. For the moment, we are
going to avoid worrying about the details of the program and only focus on
its main structure. We want the program to repeatedly read values and,
for each value read, see whether or not it satisfies the conjecture. We can
refine this analysis into something that looks more like Java (or many other
computer languages). Such a form is sometimes called pseudo-code.

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.

public static void main (String[] args)


{
int value = nextNumber();
while (value != 0)
{
testGoldbach(value);
value = nextNumber();
}
}

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.

We incorporate the ideas of Example 4 in our general solution to the prob-


lem of testing Goldbach’s Conjecture for any given positive even number n.
We repeatedly break n up into two parts. The first part should be prime
and no more than half the value read. We then test the second part to see
if it is also prime. If it is, then the conjecture is true for this value. If not,
we continue to look for other possiblities until we find a satisfactory pair
of values or we have tried all possible primes for the first part. If we never
find a pair that works, then we conclude that the conjecture is false. The
next example expresses this in pseudo-code.

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

We can now convert the pseudo-code to a Java method.

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.

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 "
+ "for " + n + " = " + firstPart
+ " + " + secondPart);
done = true;
}
else
firstPart = primeAfter(firstPart);
}
}

In order to simplify the writing of the main method, we assumed the


existence of the methods nextNumber and testGoldbach. Similarly, to
simplify the writing of testGoldbach, we have assumed the existence of
5.7. PROGRAMMING WITH METHODS 199

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;
}

A solution should always be well-documented with comments to assist


a reader. Comments for a program normally include a description of the
purpose of the program, name(s) of the author(s), and the date. Instruc-
tors may require additional items such as the course name, your student
number, the instructor’s name, and so on. Comments for each method nor-
mally include a description of its purpose, notes on any parameters, and
a description of the value returned (if any). In the next example, we have
provided such documentation to an almost complete solution to our prob-
lem of testing Goldbach’s Conjecture (lacking only the method isPrime
that tests a number to see if it is prime). The style that we have used for
comments is compatible with the style required by the Javadoc program
discussed in Appendix F. Your instructor may suggest another style.
200 CHAPTER 5. METHODS

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

* than 2, prompting repeatedly, if necessary, until a


* valid value is supplied.
*
* @return an int - either 0 or an even value > 2
*/
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;
}

/**
* 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

+ "for " + n + " = " + firstPart


+ " + " + secondPart);
done = true;
}
else
firstPart = primeAfter(firstPart);
}
}

private static boolean isPrime (int n)


{
// to come

/**
* 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

2. Complete the definition of the method isPrime whose header is


private static boolean isPrime (int n)
The method should return the value true if and only if n is a prime
number. You may assume that n > 1. Include comments in your
definition.

3. In addition to the conjecture that we have been examining in this


section, Goldbach also proposed that all odd integers greater than
six could be written as the sum of three primes. This conjecture, like
the one that we have studied, has not yet been proven or disproven.
Write a program, similar to the one that we have developed, to test
this conjecture.

5.8 Avoiding Errors and Debugging

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

2. It is sometimes difficult for beginning programmers to realize that


parameter passing is done automatically by Java. Not only is there
no need for the programmer to assign a value to a parameter on entry
to a method, it is a mistake to do so since this will destroy the value
that was passed to the parameter.
3. Remember that the value assigned to a parameter on entry to a
method is a copy of the value of the corresponding argument. Thus,
if you alter the value of a parameter, this will have no effect on the
argument.
4. If a method returns a value, be sure that it does so for every possible
situation. As an example, it is permissable to write

if (a == b)
return x;
else
return y;

but it would be an error if the method only returned a value when a


and b were equal. If you fail to write a return statement for every
possible case, the compiler will spot the error and refuse to compile
the program.

Debugging

1. To trace the actions of methods, use tracing statements at the be-


ginning of each method and just before any return statements in
the method. The tracing statements at the beginning of a method
called foo should print "entering foo" along with the values of any
parameters of the method. The tracing statements just before any
return statements should print "exiting foo" along with the values
of important local variables.
2. If a program containing methods is not executing correctly and the
methods may be the source of problems, replace the actual methods
temporarily. Queries can be set to return a constant and commands
can be set to simply print messages. The temporary code can be
turned on and off by a device similar to one suggested in the debug-
ging section of Chapter 3. Create a boolean variable DEBUG that is
5.8. AVOIDING ERRORS AND DEBUGGING 205

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?

(a) public static int largest (int a, int b)


{
// return the larger of the two parameter values
if (a > b)
return a;
else if (b > a)
return b;
}
(b) public static void applyInterest (double amount,
double i)
{
// apply interest to amount at rate i%
amount *= 1 + i/100;
return;
}
(c) public static double cubeRoot (double x)
{
// return the cube root of the parameter, x
System.out.println("What is value?")
x = In.getDouble();
return math.pow(x,1.0/3.0);
}

2. Add statements to the method range that could be used to trace its
actions.
206 CHAPTER 5. METHODS

public static int range (int n)


{
int low, high;
low = high = In.getInt();
for (int i = 2; i <= n; i++)
{
int next = In.getInt();
if (next > high)
high = next;
if (next < low)
low = next;
}
return high - low;
}

5.9 Review Exercises 5

1. Some methods return values while others do not.


(a) What is the difference between the forms of the definition of
each of these kinds of methods?
(b) What is the difference between the calls to each of these kinds
of methods?
2. Explain the difference between an argument and a parameter.
3. What is the scope of a parameter of a method?
4. In the method outline shown here, state which variables can be used
at each numbered line.

public static int sample (int a, int b)


{ // 1
int c;
... // 2
for (int d = 0; .. )
{
... // 3
5.9. REVIEW EXERCISES 5 207

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

6. Write a boolean-valued method isSquare with a single int parame-


ter, n. The method should return the value true if and only if n is
the square of some integer.

7. Complete the definition of the method digit with header


public static int digit (int n, int position)
so that the method returns the value of the digit that is position
places from the right in the decimal representation of n. As examples,
digit(763,0) should return 3
digit(8574,2) should return 5
digit(78,4) should return 0

8. Write a character-valued method convertToGrade that has an int


parameter mark. The method should return the grade that corre-
sponds to mark according to the following table.
208 CHAPTER 5. METHODS

Mark Grade
0 - 49 F
50 - 59 D
60 - 69 C
70 - 79 B
80 - 100 A
Others X

9. Write a method printTriangle that has a char parameter c and an


int parameter n. The method should print a triangular pattern with
the perimeter consisting of the character c and the interior (if there
is one) consisting of blanks. As an example, the call
printTriangle(’*’,5);
should produce output of the form
*
**
* *
* *
*****
You may assume that the value of n is positive.

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.

11. Write a method dayNumber that determines the number of days in a


year up to and including the current day. The method should have
three int parameters: year, month, and day. If the value of any
parameter is invalid, the method should print a warning message and
return the value zero. The table gives some examples of the action
of the method. Accept any non-negative year as being valid. You
may want to assume the existence of a method numberOfDays that
returns the number of days in a given month of a given year.
5.9. REVIEW EXERCISES 5 209

year month day dayNumber


2002 1 1 1
2007 12 31 365
2004 3 1 61
2011 3 1 60
2010 15 6 0

Projects

12. Triangles can be classified in a number of ways by considering the


relative sizes of either their sides or their angles. Using side classifi-
cations, a triangle is equilateral if all sides are equal; it is isosceles if
exactly two sides are equal; it is scalene if no sides are equal. Using
angle classifications, a triangle is a right triangle if its largest angle
is a right angle; it is obtuse if its largest angle is greater than a right
angle; it is acute if its largest angle is less than a right angle. If the
sides of a triangle are known, Pythagoras’ Theorem can be used to
classify the triangle as right, obtuse, or acute.

Write a program that will read an arbitrary number of sets of


three integers. The program should prompt the user for sets of num-
bers and process them until the user submits the numbers 0 0 0. For
each set of three numbers, the program should first print the val-
ues read. It should then decide whether or not the three numbers
could represent the lengths of the sides of a triangle. If the numbers
could not represent the lengths of sides of a triangle, an appropriate
message should be printed. If they could, then the program should
determine and state into which of the above classes the triangle would
be placed.

If the user provided input of


3 5 4
5 2 5
-7 1 2
0 0 0
then the program should produce output something like the following:
210 CHAPTER 5. METHODS

Provide three side lengths - 0 0 0 to terminate.


3 5 4 Triangle possible: scalene and right.
Provide three side lengths - 0 0 0 to terminate.
5 2 5 Triangle possible: isosceles and acute.
Provide three side lengths - 0 0 0 to terminate.
-7 1 2 Triangle cannot be formed.
Provide three side lengths - 0 0 0 to terminate.
0 0 0 Program was terminated by user.

13. Nim is a two-player game in which players take turns picking up


sticks. The game begins with various numbers of sticks in a number
of piles. On any turn, a player must choose a pile and remove one or
more sticks from that pile (up to the number of sticks in the pile).
The object of the game is to force your opponent to pick up the last
stick in the last pile.
Write a program that allows two people to play a game of Nim.
The program should start by creating four piles of sticks, each with a
number of sticks that varies randomly from four to eight and display-
ing the piles on the screen. The program should then prompt the first
player to remove a number of sticks from a pile. Once that player has
provided valid input, the program should then adjust the piles and
display the new configuration. This process should continue until one
player has won the game, at which time the computer should print
a congratulatory note to the winner. At the end of each game, the
computer should start a new game if the users state that they want to
do so. Once the users state that they are done, the computer should
print the number of games won by each player.
14. Pascal’s triangle is a triangular array of numbers named in honour
of the French mathematician Blaise Pascal. The first few rows of the
triangle are:

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

The triangle can be extended as far as we please. The values of


5.9. REVIEW EXERCISES 5 211

the entries in any row can be obtained in a number of ways. One


way is to note that, in any row, the first and last entries both have
the value one while the value of each interior entry is the sum of the
values in the row above it, to its left and right. As examples, in the
last row shown, 6 = 1 + 5 and 15 = 5 + 10. The values can also be
obtained without reference to other values. If we let the symbol nr
represent the number of ways of choosing r items from n different
items, then the entries in the first few rows of Pascal’s triangle are:

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)!

where the symbol n! is read as n factorial. The value of n! can be


found using the formula:

1 if n = 0
n! =
n × (n − 1) × (n − 2) × · · · × 2 × 1 if n > 0

Write a program that prints portions of Pascal’s triangle. The


program should repeatedly ask the user to supply the number of rows
in the triangle, terminating when the user supplies the value zero. In
printing a triangle, the first value in the last row should appear in
the first column and each preceding row should start printing three
spaces further right. The number of spaces between entries in a row
should be adjusted so that values are aligned correctly from row to
row. For example, if the user supplies input of 7, the program should
print the following triangle. In the bottom row of the triangle, there
are five spaces between the first 1 and the first 6 but only four spaces
between the first 6 and the first 15. In this way, the last digit of 15
is aligned with the 4 and 1 above it.
212 CHAPTER 5. METHODS

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

Classes and Objects

Up to now, our experience with objects has been lim-


ited to performing simple operations with one of Java’s
built-in types of objects — the string. We now turn our
attention to creating and using our own kinds of objects.
214 CHAPTER 6. CLASSES AND OBJECTS

6.1 Creating Objects

In mathematics, a fraction can be defined as an expression of the form ab


together with rules about how we perform operations such as addition and
multiplication on such expressions. Examples of fractions are expressions
7
like 34 , −3 , or −2
5 . In the first case, we are thinking of the class of all
fractions; in the second we are thinking of particular instances of that class.
In Java, to help us manipulate things like fractions (along with much more
complex things), we can construct classes that define the way that things
look and behave along with objects that give us instances of those classes.
As an example of these classes and objects, let us look at how we might
introduce the concept of a fraction into Java. Ignoring, for the moment, the
operations that can be performed on fractions, we can consider a fraction
to be composed of two parts: a numerator and a denominator. If we want
the numerator and denominator of our fractions to both be of type int,
we can define the class of fractions as follows:
class Fraction
{
int num;
int den;
}

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

We can think of the Fraction class as a model, blueprint, or template


of what a fraction should look like. All fractions have both a numerator and
a denominator. The class definition makes this idea explicit; it defines a
new type called Fraction that we can use in our programs. The definition
does not actually create any fractions; it simply shows us what any fractions
that we eventually create will look like. When we do create any fractions,
each one of them will have its own num and den fields. Since each instance
of a fraction will have these fields, they are, reasonably enough, called
instance fields. In a program that uses fractions, this class can appear
separately from the class that contains the main method.
Once we have defined what we mean by a fraction, we can then create
actual fractions, using our template. To begin to do this, we can, in the
main method of our program, say, make the declaration:
Fraction f;
This creates a variable f that can act as a reference to a fraction. Since
we have not assigned f a value, it does not at this point actually refer to a
fraction. To achieve this, we can now write:
f = new Fraction();
This causes Java to create an object, an instance of the type Fraction.
In addition a reference to the location in memory of the newly created
object is stored in the variable f. Pictorially, we can illustrate what we
have created as follows:
f

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;

You might find it useful to read these assignment statements as

f’s num = 2;
f’s den = 3;

Pictorially, the result of the assignments is:

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:

Fraction f1 = new Fraction();


f1.num = 3;
f1.den = 4;
Fraction f2 = new Fraction();
f2.num = 5;
f2.den = 6;

Pictorially, the result of all of this would be:


6.1. CREATING OBJECTS 217

f1 f2

Fraction Fraction

- num 3 - num 5
den 4 den 6

Once we have created objects, we can manipulate their fields in the


same ways that we do with local variables.

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.

As we have already noted, if we simply declare a reference variable,


that variable does not yet actually refer to an object. For example, if we
were to write
Fraction f1;
the result is an uninitialized Fraction variable. An attempt to use f1 in
this state will result in an error during compilation of the program. To
avoid such compilation errors, we can initialize a reference variable so that
it does not refer to an object (but is still defined) by assigning it the value
null. If we write
Fraction f2 = null;
then we can print f2 (producing the output “null”) or compare f2 to other
objects (an idea that we will explore in more detail later). The variables
f1 and f2 are illustrated in the next diagram. The dot inside f1 indicates
218 CHAPTER 6. CLASSES AND OBJECTS

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

If we were now to write


g.num = 1;
this would change the num field of the object to which g refers. Since f
refers to the same object, f would now represent the fraction 81 . We say
that g is an alias of f.

Exercises 6.1
1. What is the essential difference between a local variable and a field?

2. Write statements that could be used to create an object of type


Fraction representing the fraction 15 .

3. Assuming that the class Fraction is defined as it was in the text,


state the error in the following fragment.

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;

5. Assuming that two objects f1 and f2 of type Fraction have been


created and assigned values, write statements to perform each task.
(a) Double the value of f1.
(b) Invert f2.
(c) Make f1 equal to the (unsimplified) product of f1 and f2.
(d) Make f2 equal to the (unsimplified) sum of f1 and f2.
(e) Make f1 equal to |f1|.
6. A circle in the Cartesian plane can be described uniquely by its centre
and its radius. Thus, a class called Circle that could be used to
represent such circles might start with the following:

class Circle
{
double x; // x-coordinate of centre
double y; // y-coordinate of centre
double r; // radius
}

An object of this class, representing a circle with centre at (5, 2) and


having radius 3 is shown in the next diagram.

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

(a) Create Circle objects c1 and c2 where c1 represents a circle


with centre at (1, 2) having radius 4 while c2 represents a circle
with centre at (−2, 0) having radius 2.
(b) Set the double variable distance to the distance from the origin
to the centre of c1 and then print this value.
(c) Set the double variable centreSeparation equal to the distance
between the centres of c1 and c2 and then print this value.
(d) Set the double variable minDistance to the minimum distance
from a point on c1 to a point on c2 and then print this value.

7. A complex number z = a + bi can be specified by its real part, a, and


its imaginary part, b.

(a) Create a class Complex that could be used to represent complex


numbers.
(b) Create objects z1 and z2 that represent the complex numbers
2 + 3i and 5 − 4i.
(c) Write a fragment that assigns to z1 the sum of z1 and z2.

6.2 Instance Methods

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

There are many things to note here.


1. The method does not have the modifier static that has appeared in
every method that we have created up to now. Any method without
the static modifier is an instance method; any method having the
static modifier is a class method. We will examine the use of class
methods with objects later in the chapter.
2. The general form of a call to an instance method that returns a value
is
<object identifier>.<method identifier>(<parameter list>);
In our example, the call has the form f.size();
3. In the method, we compute the size of the fraction using the identifiers
num and den, not f.num and f.den. No object is mentioned explicitly
in the method. Instead, it is implicit here that the num and den that
we are referring to are those of the object that is currently associated
with the method. The call to the method associates the object f as
an implicit parameter of the method. If we were to call the method
with a statement like
t = g.size();
then g would be the implicit parameter and Java would use the num
and den fields of g in its calculations.
6.2. INSTANCE METHODS 223

4. The current implicit object in an instance method can be referred to


in Java as this. It would have been correct to write the statement
in the body of the method as
return Math.abs((double)this.num/this.den);
if we wanted to make explicit the fact that we are taking the quotient
of the current object’s num and den fields but there is, in this example,
no need to do so. (The next example will illustrate a practical use of
this in an instance method.)

Although the parameter list of the method in the previous example


was empty, it is possible (and common) for instance methods to have non-
empty parameter lists. We have already encountered this in the methods
of the String class. There, the header of the equals method has the form
public boolean equals (String s)
while that of the compareTo method has the form
public int compareTo (String s)
To show how we can create an instance method with a non-empty param-
eter list, suppose that we wanted a method to compare the sizes of two
Fraction objects.

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).

public Fraction larger (Fraction other)


{
if (this.size() >= other.size())
return this;
else
return other;
}

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

public void timesEquals(Fraction p)


{
num *= p.num;
den *= p.den;
}

If f and g are objects of type Fraction, then the statement


f.timesEquals(g);
would make f represent the product of the values represented by f and g
(while leaving g unchanged).

Example 3 also illustrates an important difference between object (ref-


erence type) parameters and primitive type parameters. As we have said
before, all parameters in Java are passed by value. This means that, at the
time of a call to a method, the parameter is assigned the value of the argu-
ment. Any subsequent changes to a parameter in a method cannot change
the value of the argument in the calling block. For primitive types, the
value that is passed to the parameter is the value of the item; for objects,
the value that is passed is the value of the reference to the object. Thus, for
primitive types, the parameter contains an item that has the same value
as the corresponding argument; for objects, the parameter refers to the
same object as the corresponding argument. This is illustrated in the next
diagram.

Primitive Types Reference Types

Argument Parameter Argument Parameter


a p a p
x -
value assigned
x
value assigned
-
x
- 

Same value, x Reference to same object, x

A consequence of this is that, if a field of an object parameter is altered


in a method, then this actually alters the field in the argument’s object. In
Example 3, the fields of the implicit object parameter were altered but the
226 CHAPTER 6. CLASSES AND OBJECTS

same principle applies to explicit object parameters. In either case, fields


of object parameters can be altered in methods.
Our final example illustrates the use of a method that creates an object
and returns a reference to that object.

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.

public Fraction times (Fraction other)


{
Fraction result = new Fraction();
result.num = num * other.num;
result.den = den * other.den;
return result;
}

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

Then, after execution of the statement Fraction f = g.times(h); we


would have:
6.2. INSTANCE METHODS 227

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

the += operator has for primitive numeric types. Thus, if called


by the statement
p.plusEquals(q);
(where p and q are objects of type Fraction), the method would
make p represent the sum of the fractions currently represented
by p and q while the value of q would be left unchanged.
(b) public Fraction plus (Fraction f)
The method should return a Fraction object whose value is the
sum of the implicit object parameter and the explicit parame-
ter, f. The method should leave both its explicit and implicit
parameters unchanged.
(c) public void reduce ()
The method should reduce its implicit Fraction parameter to
lowest terms. For example, if f represents the fraction 16
24 , the
statement
f.reduce();
should change f so that it represents the fraction 23 .
3. Suppose that a class Complex has been defined as follows:

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

(a) write an instance method plus that, if called by the statement


z1 = z2.plus(z3);
would set z1 to the sum of z2 and z3,
(b) write an instance method times that, if called by the statement
z1 = z2.times(z3);
would set z1 to the product of z2 and z3.

5. Consider again the class Circle.

class Circle
{
double x; // x-coordinate of centre
double y; // y-coordinate of centre
double r; // radius
}

(a) Write an instance method area that returns, as a double value,


the area of its implicit Circle object.
(b) Write a method smaller that could be called by a statement
like
c3 = c1.smaller(c2);
where c1, c2, and c3 are objects of type Circle. The method
should make c3 refer to the smaller of the circles represented by
c1 and c2 (or c1 if c1 and c2 are the same size).
(c) Write a boolean-valued instance method isInside that could
be called by a statement like
boolean contained = c1.isInside(c2);
The method should return true if c1 is entirely inside c2 and
return false otherwise.

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

In creating objects of the type Fraction, we have been using statements


that have a form like the following.
Fraction f = new Fraction();
The parentheses in the expression Fraction() make it look like a method
call and, in fact, it is. The method is called a constructor method. We did
not create such a method but, for every class, Java automatically creates
a default constructor method that has the same identifier as the class.
Constructor methods are instance methods used to initialize objects
at the time the objects are created. The default method initializes any
numeric fields to zero, boolean fields to false, and reference fields to the
value null. We can, however, write our own constructors that do whatever
initialization we wish. As an example, we can use a constructor to initialize
objects of type Fraction as follows:

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

There are a number of things about constructors that the example


displays.

1. The identifier of the constructor method is exactly the same as the


identifier of the class; both are called Fraction.

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.

3. The constructor is an instance method. Thus, it can refer to the


instance fields of the class (num and den), just as any other instance
method can.

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);

5. The parameters to the constructor method were called n and d so


that their identifiers did not conflict with num and den, the identifiers
of the object that is implicitly associated with the method. If we had
wanted to use num and den as identifiers of the explicit parameters,
we could have written the definition as follows:

public Fraction (int num, int den)


{
this.num = num;
this.den = den;
}

As usual, this means the current implicit object. The identifiers on


the left side of the assignment statements refer to the this object’s
num and den fields while the identifiers on the right side refer to the
explicit parameters to the method.

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

If a class has multiple constructors, it is sometimes possible to use the


code from one constructor in another. The next example illustrates this
idea.

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

public Student (String n, String sn)


{
name = n;
studentNumber = sn;
credits = 0;
}

Sometimes a new student will already have some credits from another
school. To handle such cases, we could write another constructor.

public Student (String n, String sn, int c)


{
name = n;
studentNumber = sn;
credits = c;
}

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.

public Student (String n, String sn)


{
this(n,sn,0);
}
6.3. CONSTRUCTORS 235

In Example 5, we have used this in a new way, as a reference to the con-


structor of the current implicit object. If a constructor contains a call to
another constructor, that call must be the first statement of the construc-
tor.

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.

2. For the class Circle defined as follows:

class Circle
{
double x; // x-coordinate of centre
double y; // y-coordinate of centre
double r; // radius
}

(a) Write a constructor method that has no parameters. The method


should construct a Circle object with centre (0, 0) and radius
1.
(b) Write a constructor method that has three parameters represent-
ing the coordinates of the centre and the radius of the object to
be constructed.
(c) Write a constructor method with a parameter, an object of type
Circle. The method should construct a new Circle object with
the same field values as those of the parameter.
236 CHAPTER 6. CLASSES AND OBJECTS

6.4 Hiding Information


Until now, we have been declaring all the fields of our classes in a form
similar to that used for local variables in methods. It is possible, however,
to declare fields with visibility modifiers, just as we have seen for methods.
If we were to declare a field with the modifier public, then that field
would be openly available to be read and altered from within any class in
any package. By omitting the public modifier, we have given the fields
package visibility — available for inspection and alteration from within any
class in the package in which the class is defined. If we have not created
a package for our classes, then package visibility is equivalent to directory
visibility. That means that omission of any visibility modifier makes the
field available from anywhere in the current directory.
Frequently, however, we do not want to give direct access to fields of
a class from any point outside the class. To illustrate this, let us consider
once again our Fraction class. In mathematics, fractions are almost always
a
written with positive denominators; fractions of the form −b are usually
rewritten as b while fractions of the form −b are simplified to ab . Suppose
−a −a

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.

If we want to make sure that Fraction objects stay in this form, we


cannot allow everybody to have direct access to the num and den fields.
To control access to the fields, we can declare them as shown in the next
fragment:
private int num;
private int den;
Now the fields cannot be seen or altered from outside the Fraction class.
Since the constructor method is inside the Fraction class, it can change
the values of private fields within that class.
If we make the fields of our Fraction class private, it is not only
impossible for outsiders to make direct changes to these fields, it is also
impossible to determine directly, from outside the class, what values are
stored in the fields. If we wish to permit private fields of a class to be
seen from outside the class, we can again use methods.

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.

public void invert ()


{
if (num == 0)
throw new RuntimeException
("Attempt to invert Fraction with value zero");
else
{
int temp = num;
num = den;
den = temp;
if (den < 0)
{
6.4. HIDING INFORMATION 239

den = -den;
num = -num;
}
}
}

Although it may appear to be something of a nuisance to have to


go through channels like accessor and mutator methods, these devices do
usually pay dividends in the end. We may not have convinced you yet, but
we will try to do so as we proceed.

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;

public void foo (Sample s)


{
...
s.i = 5; // ***
...
}
}

The statement marked with the asterisks attempts to directly alter


the value of a private field. State, with reasons, whether or not the
statement is valid.
3. Write a constructor method for the Circle class discussed earlier in
this chapter. The method should take three double parameters, the
coordinates of the centre and the radius. The method should ensure
240 CHAPTER 6. CLASSES AND OBJECTS

that the circle’s radius is not negative by changing the sign of any
negative radius parameters.

6.5 Comparing and Displaying Objects

As we have already seen with assignment statements, operations on objects


can sometimes have results that are very different from the corresponding
operations with primitive types. In this section, we will explore some other
operations with objects.
Before looking at new operations, let us recall the way that assignment
statements work with reference variables.

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

If we wanted f and g to refer to different objects that represented the


same fraction, we would have had to proceed somewhat differently.

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

In this situation, if we were to write


g.setNum(1);
this would change the num field of the object to which g refers. Since f now
refers to a different object, that object would not be changed.
242 CHAPTER 6. CLASSES AND OBJECTS

In comparing objects, we must again be conscious of the fact that


object variables are references.

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.

If we want to perform a comparison of two objects based on the con-


tents of their fields, we usually do what we have been doing with strings
— use a boolean-valued instance method called equals. To be consistent
with the equals methods in the classes of Java’s API, any equals method
that we write should be an instance method with one explicit parameter
that returns false if the explicit parameter has the value null.

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

an instance method be given an instance of an object as an implicit parameter and null


is not considered to be an instance of an object. Any attempt to use null as the value
of an instance variable would produce an error message.
6.5. COMPARING AND DISPLAYING OBJECTS 243

public boolean equals (Fraction other)


{
if (other!=null && num==other.num && den==other.den)
return true;
else
return false;
}

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.

public boolean equals (Fraction other)


{
return other!=null && num==other.num && den==other.den;
}

We could use this method in contexts like the following:


if (p.equals(q)) . . .

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:

public static void main (String[] args)


{
Fraction f = new Fraction(2,3);
System.out.println(f);
}

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

The toString method that Java provides as a default returns a string


that contains the identifier of the class together with a memory reference to
the current object. The memory reference may vary from one machine to
another and from one day to another on the same machine. As we did with
the default for the equals method, we can override the default toString
method with our own.

Example 6
Suppose we add the following method to our Fraction class.

public String toString ()


{
return num + "/" + den;
}

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?

2. Suppose that p and q are both of type Fraction with p representing


2 1
3 and q representing 6 .

(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

(a) Write a fragment to create a new reference, c2, to the same


object.
(b) Write a fragment to create a new Circle object c3, with the
same centre and radius as c1.
(c) Draw diagrams to illustrate the results of executing the code in
parts (a) and (b).
(d) What is the value of the expression c1 == c2?
246 CHAPTER 6. CLASSES AND OBJECTS

(e) What is the value of the expression c1 == c3?


(f) Write a boolean instance method called equals that returns
true if and only if one Circle object is equal to another one.
(g) Write a toString method for the Circle class. For a Circle
object with x = 3, y = -4, and r = 2, the toString method
should return a String with the value:
"centre: (3,-4) radius: 2".

4. You have been hired by a wicked witch with a strong interest in


children. The program that she uses to help her maintain records
has a class Child that contains the fields
private int height; // height in cm
private double mass; // mass in kg
The witch wants you to write an equals method for the class. The
witch considers two Child objects to be equal if their heights differ
by no more than 2 cm and their masses differ by no more than 0.5
kg.

5. Write a definition of an equals method for the Fraction class. Your


method should return true if and only if the Fraction objects being
compared represent equivalent fractions.

6.6 Class Methods

Although we usually use instance methods in our dealings with objects,


it is possible (and sometimes more appropriate) to use class methods —
methods that have the same form as those that we encountered in Chapter
5, with the word static in their headings.
As an example, suppose we want to create a class method for our
Fraction class that adds two objects of type Fraction and places the sum
in a Fraction.
6.6. CLASS METHODS 247

Example 1
The class Fraction can be extended to contain an addition method as
follows:
class Fraction
{
private int num;
private int den;

public static Fraction sum (Fraction f1, Fraction f2)


{
Fraction total = new Fraction();
total.num = f1.num*f2.den + f2.num*f1.den;
total.den = f1.den*f2.den;
return total;
}
}

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

(c) The method isPositive should have one Fraction parameter.


It should return a boolean value: true if its parameter repre-
sents a positive fraction and false otherwise.

6.7 Class Fields

We have seen that methods can be written as either instance methods or


class methods (using the modifier static). We have also seen the use of
instance fields in classes. Now we complete this part of the picture of Java
by discussing class fields.
As with class methods, a class field is created by using the modifier
static in its declaration. The primary difference between a class field and
an instance field is that there is only a single copy of a class field but there
is a copy of each instance field for every instance of an object that is created
from the class. Like instance fields, class fields are initialized automatically.
Class fields are useful for quantities that can be shared by the members
of the class. The next example illustrates this.

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

We refer to class fields in a manner similar to that used for class


methods. Since a class field is not associated with any object, we refer
to it from within its class simply by using its identifier. In the previous
example, from within the Car class we would refer to the class field for the
price of gas as gasPrice. If a class field is not declared to be private, we
can refer to it from outside the class using the form
<class identifier>.<field identifier>
If the gasPrice field in the previous example had been declared to be
public, we could refer to it from outside the Car class as Car.gasPrice.
Class fields are often used for constants associated with a class. For
example, if we had a class called Elevator, we might want to specify the
maximum load that could be carried by any elevator. To do so, we might
want to write the declaration
public static final int CAPACITY = 1800; // max load in kg
From within the Elevator class, the field could be called CAPACITY while
outside the Elevator class, the field would have to be referred to as
Elevator.CAPACITY. We have already encountered such class fields — both
Math.PI and Math.E are constant class fields in the Math class.
Notice in the declaration that we made the field public. This is com-
mon with constant fields. We do not need to encapsulate the data to
protect it because making it final prevents anybody from tampering with
the value that we initially assign to it.

Exercises 6.7
1. What is the difference between the declaration of a class field and
that of an instance field?

2. How do we specify that a field’s value cannot be altered?

3. State the convention that is used to distinguish constant class field


identifiers from variable class field identifiers.

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?

5. (a) Write an accessor method called getPrice for the gasPrice


field shown in Example 1 of this section.
6.8. PUTTING THE PIECES TOGETHER 251

(b) Write a mutator method called setPrice to assign a new value


to the gasPrice field.
(c) Write a statement that could be used to set a variable current-
Price to the value of the gasPrice field.
(d) Write a statement that could be used to set the gasPrice field
to the value 72.9.
6. Suppose that a class Account is to be created to maintain records
of savings accounts in a bank. State, with reasons, which of the
following should be class fields and which should be instance fields.
(a) minimum balance (b) account number
(c) current balance (d) interest rate

6.8 Putting the Pieces Together

Throughout this chapter, we have been using Fraction objects to illustrate


our ideas. In this section we show how one might assemble and use the
various pieces that we have been developing.

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;
}

public Fraction (int n, int d)


{
// Create an object whose value is n/d.
num = n;
den = d;
this.reduce();
}

public Fraction (Fraction f)


{
// Create an object identical to the parameter f
num = f.num;
den = f.den;
}

public Fraction plus (Fraction other)


{
// Create and return a Fraction object with value:
// this + other
Fraction result = new Fraction(num*other.den
+ den*other.num, den*other.den);
return result;
}

public Fraction minus (Fraction other)


{
// Create and return a Fraction object with value:
// this - other
Fraction result = new Fraction(num*other.den
- den*other.num, den*other.den);
return result;
}
6.8. PUTTING THE PIECES TOGETHER 253

public Fraction times (Fraction other)


{
// Create and return a Fraction object with value:
// this * other
Fraction result = new Fraction(num*other.num,
den*other.den);
return result;
}

public Fraction dividedBy (Fraction other)


{
// Create and return a Fraction object with value:
// this / other
Fraction result = new Fraction(num*other.den,
den*other.num);
return result;
}

public static Fraction getFraction (String prompt)


{
// Prompt the user for the fields of a fraction and
// return a new Fraction object (in reduced form).
Fraction result = new Fraction();
System.out.println(prompt);
System.out.println("numerator: ");
result.num = In.getInt();
System.out.println("denominator: ");
result.den = In.getInt();
result.reduce();
return result;
}

public String toString ()


{
// Return a string representing the implicit Fraction
// object. If the object represents an undefined or
// indeterminate value, return an appropriate string.
if (den != 0)
return num + "/" + den;
254 CHAPTER 6. CLASSES AND OBJECTS

else if (num == 0)
return "NaN";
else if (num > 0)
return "Infinity";
else
return "-Infinity";
}

public int getNumerator ()


{
// An accessor method for the numerator field
return num;
}

public int getDenominator ()


{
// An accessor method for the denominator field
return den;
}

public boolean equals (Fraction other)


{
// Return true if and only if the fields of the implicit
// Fraction object are identical to those of other
return other!=null && num==other.num && den==other.den;
}

public double size ()


{
// Return the magnitude of a Fraction object
return Math.abs((double)num/den);
}

private void reduce ()


{
// Reduce the implicit Fraction object to lowest terms
// and ensure that the denominator is not negative.

// eliminate negative denominator


if (den < 0)
6.8. PUTTING THE PIECES TOGETHER 255

{
den *= -1;
num *= -1;
}

// store zero as 0/1


if (num == 0 && den != 0)
den = 1;

// store infinities as 1/0 or -1/0


if (den == 0 && num != 0)
if (num > 0)
num = 1;
else
num = -1;

// eliminate any common factors in num and den fields


for (int i = Math.min(Math.abs(num),den); i >= 2; i--)
if (num % i == 0 && den % i == 0)
{
num /= i;
den /= i;
}
}
}

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

Fraction sum = new Fraction();


Fraction factor1 = new Fraction(1,1);
Fraction factor2 = new Fraction(1,2);
for (int i = 1; i <= n; i++)
{
Fraction term = factor1.times(factor2);
sum = sum.plus(term);
factor1 = factor2;
factor2 = new Fraction(1,i+2);
}
System.out.println("Sum of " + n + " terms: " + sum);
}
}

If a user provides input of 10, the program will print


Sum of 10 terms is 10/11

In each of the last two examples, the resulting program contained


two classes: the Fraction class and another class containing the main
method. In programs containing more than one class, we can organize
the files containing these classes in a number of ways. One possibility is
to place each class in a separate file. In this case, the file containing the
class Sample should be called Sample.java. Alternatively, multiple classes
can be placed in one file. In this case, exactly one class should contain
the main method. The file name must refer to that class. For example, if
the class containing main is called Driver, then the file name should be
Driver.java.
The visibility modifier public can be used with classes, as it is with
methods and fields. If no visibility modifier precedes a class definition, then
the class has package visibility. If no package has been created, this means
that the class is visible from within the current directory. On the other
hand, if we declare a class to be public, then it can be seen from anywhere.
If a file contains more than one class, then only one can be declared public
and the file name must refer to this class. Combining some of these rules,
we can see that, if one of the classes of a multi-class file contains a main
method, then only this class can be declared public.
258 CHAPTER 6. CLASSES AND OBJECTS

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

6. Modify the toString method of the Fraction class so that, if the


magnitude of the numerator is equal to or greater than that of the
denominator, the method returns a string that has the form of ei-
ther an integer, if appropriate, or a mixed fraction. The table shows
examples.
Value of num Value of den String Returned
3 5 "3/5"
13 3 "4 and 1/3"
20 4 "5"
-11 6 "-1 and 5/6"
-15 5 "-3"
6.9. AVOIDING ERRORS AND DEBUGGING 259

6.9 Avoiding Errors and Debugging

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.

• Local Variables A local variable is declared inside a block in


a method. It is not automatically initialized by Java; this is the
responsibility of the programmer. Its scope is from the point at
which it is declared to the end of the block containing the decla-
ration. Local variables declared in the initialization expression
of a for statement are defined only within the for statement.
The life of a local variable begins when its declaration state-
ment is executed and it ends when the program exits the block
in which the variable was declared.
• Parameters A parameter is declared in the header of a method.
It is automatically given, as an initial value, the value of the cor-
responding argument in the call to the method. The scope of
a parameter is the body of its method. The life of a parame-
ter begins when the program begins executing the parameter’s
method and ends when the program returns to the point from
which it was called.
• Instance Fields An instance field is declared within a class but
outside any method of that class. Instance fields are automati-
cally initialized (numeric fields to zero, boolean fields to false,
and reference fields to null). There is one copy of an instance
field for each object (instantiation) of that class. The scope of an
instance field depends on any visibility modifier that might be
applied to it with private fields being visible only in the class
in which they are defined. Visibility of a field may be interfered
with by a local variable with the same identifier. (See the first
item in the Debugging section of this chapter for an explanation
260 CHAPTER 6. CLASSES AND OBJECTS

and example.) The lifetime of an instance field is the lifetime of


the object with which it is associated.
• Class Fields A class field, like an instance field, is declared
within a class but outside any method of that class. A class
field is also automatically initialized in the same way and has the
same visibility rules. A class field is declared with the modifier
static and there is only one copy of the field for the entire class.
The life of a class field matches the life of its class.

2. Remember that there are two parts to the creation of an object:


declaration of a variable that acts as a reference to an object of the
appropriate type and the creation of the object itself. For example,
consider these statements:
Fraction f;
f = new Fraction();
The first statement creates f, a variable that can act as a reference
to a Fraction object and the second statement invokes a constructor
to create a Fraction object to which f refers. The two operations
can be combined into one statement
Fraction f = new Fraction();
but you should be clear about the two operations that are being
performed by this statement.

3. Remember that Java supplies a default constructor only if we have


not created any constructor of our own. If you write a constructor
that has one parameter, then you can no longer use a constructor for
that class that has no parameters unless you write one yourself.

4. We noted earlier in this chapter that the == operator, when applied


to references to objects, only returns true if the references are to the
same object. To compare objects, you should usually use an equals
method that compares the values of the fields of objects. Be sure
to write your own equals method to override the default that Java
provides as the default only uses == as its criterion for equality.

5. Normally, an expression that invokes an instance method takes the


form
<object identifier>.<method identifier>(<parameter list>)
Within an instance method, however, the <object identifier> can be
omitted. In that case, Java assumes that the associated object is the
current implicit object (which we can refer to using this). It may
6.9. AVOIDING ERRORS AND DEBUGGING 261

make your intentions clearer to your readers to write such a call using
an expression of the form
this.<method identifier>(<parameter list>)

Debugging

1. If a method that assigns values to fields of a class does not seem to be


working correctly, check to see that you have not declared the fields
within the method. If you do so, then the identifiers will be taken to
be local variables, not the fields of the class. As an example, consider
a constructor of the Fraction class, to be called by a statement like
Fraction f = new Fraction(2,3);
A correct way to write the constructor would be
public Fraction (int n, int d)
{
num = n;
den = d;
}
where num and den are fields of the Fraction class. It is a common
mistake to write such a constructor as follows.
public Fraction (int n, int d)
{
int num = n;
int den = d;
}
This will not have the desired effect because num and den are local
variables. The fields with the same identifiers will not be assigned the
appropriate values; they will both be left with the default value of
zero. We say that the local variables num and den shadow the fields
with the same identifiers (because the local variables throw the fields
into the shadows where we cannot see them).
2. As we suggested earlier, it is a good idea in developing and testing
the methods of a class, to write a toString method for the class.
Once this is done, it makes the task of tracing the activities of other
methods much easier.
262 CHAPTER 6. CLASSES AND OBJECTS

Exercises 6.9
1. Under what circumstances does Java supply a default constructor for
a class?

2. Under what circumstances will Java’s default equals method return


the value true?

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.

public int crossProduct (Fraction f)


{
reduce();
f.reduce();
return this.num*f.den + this.den*f.num;
}

(a) What object is being reduced in the first call to reduce?


(b) How else could that call have been written?
(c) How could the return statement be shortened?

6.10 Review Exercises 6

1. What is the difference between a class and an object?

2. What is the difference between the declaration of a class field and the
declaration of an instance field?

3. What is the difference between a call to a class method and a call to


an instance method?

4. Suppose that a complex number z = a+bi is represented by an object


of the form shown in the diagram
6.10. REVIEW EXERCISES 6 263

Complex

- re a

im b

Write a definition for a class Complex that contains

(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.

5. Suppose that the following fragment is part of a method in the


Complex class developed in the previous question. Draw diagrams
to illustrate the result of executing the fragment.

Complex z1, z2, z3;


z1 = new Complex(2,-2);
z2 = new Complex();
z3 = z1;
z2.re = z3.im + 3;
z2.im = 2*z1.im;
z3.im = z1.re / 2;

6. Write an instance method harmonicSum for the Fraction class dis-


cussed throughout this chapter. The method should have one int
parameter, n. It should set its implicit parameter to the value of the
264 CHAPTER 6. CLASSES AND OBJECTS

fraction found by adding the terms of the harmonic series


1 1 1
1+ + + ···+
2 3 n
The fraction should be reduced to lowest terms. If n < 1, then the
fraction should be set to 01 .

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.

• public Lock (int key)


Create a lock that is initially open.
• public void close ()
Close the lock.
• public boolean isOpen ()
Return true if and only if the lock is open.
• public void open (int key)
Open the lock if and only if the parameter key matches the lock’s
own key. If the lock is closed and the keys do not match, count
the failed attempt. If the same lock receives three or more failed
attempts in a row, print the message "ALARM".

The following main method illustrates how the Lock class should
work.

public static void main (String[] args)


{
Lock lock1 = new Lock(111);
Lock lock2 = new Lock(222);

lock1.close();
lock2.close();

System.out.println(lock1.isOpen()); // prints false


lock1.open(123); // fails to open lock1
lock1.open(456); // fails to open lock1
lock2.open(222); // opens lock2
lock1.open(789); // fails - prints ALARM
6.10. REVIEW EXERCISES 6 265

lock1.open(111); // opens lock1


}

Projects

8. Suppose that rectangles in a Cartesian plane are represented by a


class Rectangle that has four private fields as shown:

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

(a) Write a constructor method that has four parameters represent-


ing the four fields of the class. The constructor should replace
any negative length parameters with zero.
(b) Write a toString method for the Rectangle class. For the rect-
angle with lower left corner located at (3, 2) and having width 4
and height 5, the method should return "base:(3,2) w:4 h:5".
(c) Write an instance method, area, that returns the area of a rect-
angle.
(d) Write an instance method, perimeter, that returns the perime-
ter of a rectangle.
(e) Write a class method, intersection, that has two Rectangle
parameters. The method should return the rectangle formed by
the area common to the two rectangles. If they do not intersect,
the method should return the rectangle whose instance fields
are all zero. If the rectangles touch, the method should return
a “rectangle” of zero width or zero length.
(f) Write a class method, totalPerimeter, that has two Rectangle
parameters. The method should return the total perimeter of
the figure formed by the two rectangles. It should only count as
part of the perimeter those portions of the edges of a rectangle
that are on the exterior of the resulting figure. If one rectangle
is completely contained by the other, then the method should
266 CHAPTER 6. CLASSES AND OBJECTS

return the perimeter of the outer rectangle. If the rectangles


do not intersect, then the method should return the sum of the
individual perimeters.
(g) Write an instance method, contains, that has one explicit pa-
rameter of type Rectangle. The method should return true if
every point of the rectangle determined by the explicit param-
eter is on or within the rectangle determined by the implicit
parameter. It should return false otherwise.

9. Java provides a class BigInteger in the package java.math that


allows us to perform arithmetic on integers that are arbitrarily large.
The class includes the following features (plus many others):

• Two class fields contain the BigInteger representations of zero


and one.
public static final BigInteger ZERO
public static final BigInteger ONE
• There is a constructor that creates a BigInteger from a given
string. It has the header
public BigInteger (String s)
The string s consists of a sequence of one or more decimal digits
that may be preceded by an optional minus sign. No other
characters are permitted.
• Instance methods for performing arithmetic include the follow-
ing:
public BigInteger add (BigInteger other)
public BigInteger subtract (BigInteger other)
public BigInteger multiply (BigInteger other)
public BigInteger divide (BigInteger other)
public BigInteger remainder (BigInteger other)
public BigInteger gcd (BigInteger other)
In each case, the method whose identifier is <op> returns a
BigInteger whose value is this <op> other. The method gcd
returns the greatest common divisor of this and other.
• A method compareTo compares two BigInteger values. The
method has header
public int compareTo (BigInteger other)
The method returns −1, 0, or 1 depending on whether the im-
plicit BigInteger object is less than, equal to, or greater than
6.10. REVIEW EXERCISES 6 267

other. It can be used to implement any of the six relational


operators (<, <=, >, >=, ==, or !=) by writing expressions of the
form
a.compareTo(b) <op> 0
• A toString method returns a string representation of a Big-
Integer value. The method has the header
public String toString ()
• An equals method tests two BigInteger objects for equality.
The method has the header
public boolean equals (BigInteger other)
Use the BigInteger class3 to create a BigFraction class. The class
should contain features that parallel those in the Fraction class of
Section 6.8 (Putting the Pieces Together). Write a main method, in
another class, that tests your BigFraction class thoroughly.
10. In 1843 William Rowan Hamilton extended the notion of a complex
number to that of a quaternion. Complex numbers contain the value
i with the property that i2 = −1 while quaternions contain values i,
j, and k with the following properties:

i2 = j 2 = k 2 = ijk = −1
ij = −ji = k
jk = −kj = i
ki = −ik = j

Just as a complex number z can be written in the form z = a + bi


where a, b ∈ R, a quaternion q can be written in the form q = w +
xi + yj + zk where w, x, y, z ∈ R. We can also represent a quaternion
as an ordered quadruple of real numbers [w, x, y, z]. Sometimes it is
useful to represent a quaternion as a combination of a scalar s = w
and a vector ~v = [x, y, z]. Thus, the following are all equivalent:

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

We will be interested in a number of operations involving quaternions.


• Addition
Given two quaternions

q1 = [w1 , x1 , y1 , z1 ] = (s1 , ~v1 )


q2 = [w2 , x2 , y2 , z2 ] = (s2 , ~v2 )
their sum is:

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

cq = cw + cxi + cyj + czk

• 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

Quaternions are useful in computer graphics for the representation


and calculation of rotations of graphical objects. Suppose that we
are given a point P = (x, y, z) and we want to determine its image
P 0 after a rotation through an angle θ about a direction defined by
a unit vector û. We first obtain a quaternion representation of the
point P by letting

p
~ = [x, y, z]
and p = (0, p
~)

θ
Then let s = cos
2
θ
and ~v = û sin
2
and q = (s, ~v )

If we now find the product

p0 = qpq−1

this product will have the form

p0 = (0, ~p 0 )

~ 0 are the coordinates of the image P 0 of


where the components of p
the original point P .

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;
..
}

You are to write the following constructors and methods to be added


to the class:
6.10. REVIEW EXERCISES 6 271

• A constructor method that has four parameters representing the


fields w, x, y, and z.
• A constructor that has two parameters: a double value repre-
senting the scalar part of a quaternion and a Vector3 object
representing the vector part.
• A constructor with no parameters that creates an instance of a
unit quaternion, with a scalar of one and vector components all
set to zero.
• A toString method that returns a string containing the object’s
components as a scalar, s, and a vector, ~v . For the quaternion
q = [3.23, 2, −0.625, 3.56], the method would return the string
"s: 3.23 v: [2.0,-0.625,3.56]"
• An instance method conjugate that returns the conjugate of
its implicit parameter.
• An instance method magnitude that returns the magnitude of
a quaternion.
• An instance method times that returns a Quaternion object
which is the product of the implicit object times the explicit
object (in that order).
• An instance method inverse that returns the inverse of its im-
plicit parameter.
• An instance method getVector that returns, as a Vector3 ob-
ject, the vector portion of its implicit quaternion object.
• A class method image that finds the image P 0 of a point P under
a rotation. The header of the method is
public static Vector3 image
(Vector3 p, Vector3 u, double theta)
Here p is a vector whose components are the coordinates of the
point P , u is a vector giving the direction of the axis of the
rotation, and theta is the angle, in radians, through which the
rotation is to take place. The vector returned by the method
has components that are the coordinates of the image point P 0 .
You should create your own main method to test your classes thor-
oughly.
Real Jobs — Real People
Name: Rhea Plosker
Title: Independent Consultant
Company: On Contract to Kraft Foods
Education: B.A.Sc. Industrial Engineering,
University of Toronto (1986)
M.A.Sc. Operations Research,
University of Toronto (1991)

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

Until now, we have been studying classes and objects


more or less in isolation but an important aspect of ob-
jects is the many ways that they can interact with each
other. In this chapter, we examine some of the rela-
tionships that may exist among classes and objects in
Java.
276 CHAPTER 7. OBJECT INTERACTION

7.1 Class Hierarchies

To begin to examine some of the relationships among classes and objects,


we use an analogy. A dictionary might define a Samoyed as a dog of
medium size with a thick white coat, erect ears, and a tail curling over
the back. Even if you had never heard of a Samoyed before, these few
words would give you a very good idea of what a Samoyed is. The key
to giving such a clear picture with such brevity is the word dog. As soon
as you know that a Samoyed is a kind of dog, you already have a great
deal of information about it. All that the rest of the definition has to do
is make clear what distinguishes a Samoyed from other dogs. We say that
the Samoyed inherits characteristics from dogs.
To take this idea closer to the meaning of inheritance in Java, let
us consider Dog and Samoyed as classes. Then Dog is the superclass of
Samoyed. Examining the relationship in the opposite direction, we can say
that Samoyed is a subclass of Dog or Samoyed extends Dog. A class like
Dog can have many subclasses. Subclasses of Dog would include Husky,
Beagle, and so on. On the other hand, in Java, each class can have only
one superclass.1
Although a class can have no more than one superclass, it can in-
herit from many classes. Extending our example, we might say that the
superclass of Dog is Mammal. Similarly, the superclass of Mammal might be
Vertebrate and the superclass of Vertebrate might be Animal as shown
in the next diagram.

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

Samoyed Husky Beagle

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

we use an example that we will be referring to frequently.

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

Object Object Object

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

(c) From what class or classes does Husky inherit?


(d) Extend the diagram to include Invertebrate, Human, and Insect.
(e) Suggest names of two other subclasses of Vertebrate.

2. If you were to write a definition of each of the following, what class


would you extend? (Do not actually write the definitions.)

(a) Carrot (b) Bungalow

(c) Flute (d) Neutrino

3. Does every Java class have a superclass? Explain.

4. Explain the difference between the statements “ClassA extends ClassB”


and “ClassA inherits from ClassB”.

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.

7.2 Inheritance and Variables

Among other things, inheritance forces us to reconsider the idea of a ref-


erence variable’s type and the types of values that can be assigned to it.
In the previous chapter, if we declared a variable to be of type Fraction,
then the only values that we assigned to it were Fraction objects. Now,
however, we see that objects can have parts of different types. In such
a case, Java allows us to have a reference variable of type T refer to any
object that has a T part.
280 CHAPTER 7. OBJECT INTERACTION

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).

Student s = new Student();


Person p = new Student(); // a Student has a Person part
Object o = new Student(); // a Student has an Object part

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.

Student s = new Student();


Object o = s;

In the second statement, s refers to a Student object that has an Object


part. Any assignment upward in a hierarchy is valid. On the other hand,
the following fragment is not correct.
7.2. INHERITANCE AND VARIABLES 281

Object o = new Object();


Student s = o;
The compiler objects to the second statement because the right hand side
is of type Object and does not have a Student part. It is, therefore, wrong
to attempt to have the variable s, of type Student, refer to the object o.
Things can get confusing if we create references to objects of different
types. As an example, consider the fragment
Object o = new Student();
Student s = o;
The first statement is valid (because a Student has an Object part) but
the second statement presents the compiler with a problem because it is
attempting to make an assignment of an Object (that might not have a
Student part) to a variable of type Student. By looking at both lines of
the fragment, we can see that, in fact, the variable o refers to an object
that does have a Student part but the compiler does not look at previous
lines of code and it produces an error message.
To get around such problems, we use a device that we have seen before
— a cast. We first encountered the concept of a cast with primitive types.
With such types, a cast is required for an assignment that involves a nar-
rowing type conversion — a conversion from one type to a more restrictive
type (such as double to int). A similar situation holds with assignments
of objects of different types. First, we can never assign an object to a
reference variable of another type unless the two types are in the same
class hierarchy. Second, if an assignment is to be made to a different type
within the same hierarchy, it must use a cast if the assignment is made to a
more restrictive type of object (sometimes called downcasting because the
target is lower in the hierarchy). The following fragment is both valid and
acceptable to the compiler.
Object o = new Student();
Student s = (Student)o;
The cast of o to a Student does not alter o. It simply reassures the compiler
that it need not worry about the assignment by telling it that o refers to
an object that contains a Student part.
Of course, when using a cast, it is important to be sure that the cast
makes sense. To show what we mean, consider the next fragment.
Object o = new Object();
Student s = (Student)o;
282 CHAPTER 7. OBJECT INTERACTION

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

• b is visible in the package in which Sample is defined

• c is visible within Sample and any of its subclasses

• d is visible only within Sample


7.2. INHERITANCE AND VARIABLES 283

As we have noted here, fields that are not declared to be private


are, normally, visible from subclasses. If, however, we declare a field in a
subclass that has the same identifier as a field in its superclass, then the
field in the superclass is shadowed by the one in the subclass; it is no longer
visible in the subclass. The two fields need not be of the same type; as long
as the identifiers are the same, the field in the subclass shadows the other.
This is very similar to the process of shadowing we saw in Chapter 6 with
local variables shadowing fields in the same class.

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

Car Truck Bus


6

LightTruck HeavyTruck

(a) Vehicle v = new Car();


(b) Truck t = new Truck();
(c) Bus b = new MotorVehicle();
(d) Object o = new Truck();
(e) Car c = new Bus();
(f) Bus b = new Vehicle();
284 CHAPTER 7. OBJECT INTERACTION

(g) MotorVehicle m = new LightTruck();

2. Referring to the classes shown in Example 1 of Section 7.1, which of


the following conversions would require a cast?
(a) Dog to Husky (b) Mammal to Animal
(c) Dog to Animal (d) Vertebrate to Beagle

3. Using the classes shown in Example 1 of Section 7.1, suppose that we


construct the following variables.
Mammal pet = new Dog();
Dog toby = new Samoyed();
Dog sampson = new Dog();
Consider the following statements. For each one, state whether it is
valid, incorrect but fixable with a cast, or not valid and not fixable
with a cast. If a cast will make the statement valid, state the cast.
(a) Samoyed s = pet; (b) Object o = sampson;
(c) Samoyed s = toby; (d) Dog d = pet;
(e) Mammal m = toby; (f) Samoyed s = sampson;

7.3 Inheritance and Methods

Although we did not use the term inheritance explicitly in Chapter 6, we


certainly used some of its features. For example, we said that, for any class
of objects that we define, Java provides a toString method for that class.
Now we can see how this works. The toString method that Java supplies
is defined in the class Object. Since all classes inherit from Object, objects
of any type will always have an Object part and so they can all use the
toString method defined there. In the same way, all classes can use the
equals method defined in the Object class.
Recall that the default toString method (defined in the Object class)
returned a string that contained the identifier of the class and a memory
reference to the current object of the class. To make the toString method
more useful, we could override the default definition in the Object class
with one that had the same signature but whose functionality was more
7.3. INHERITANCE AND METHODS 285

appropriate. For example, we wrote a toString method for the Fraction


class that returned the numerator and denominator of a fraction. We did
the same sort of thing with the equals method, overriding the definition of
equals in the Object class with our own, more useful definitions. The next
diagram illustrates this for the Fraction class. The dots indicate methods
that we have not shown.

A Fraction Object
Object
toString()
equals(Object o)
...

Fraction
toString()
equals(Fraction f)
...

Generally, when a call is made to an instance method, Java looks for


a method with the appropriate signature starting from the lowest class in
the hierarchy of the object. This is illustrated in the next example.

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.

As we noted in the previous section, the type of a reference variable


need not be the same as the object to which it refers. When we use methods
in such cases, we must be very careful to see which method is invoked.
To illustrate this, let us look again at our Object – Person – Student
hierarchy. Suppose now that we have written equals methods for the
286 CHAPTER 7. OBJECT INTERACTION

classes Person and Student. Suppose further that we have constructed


objects with the following statements.
Object o = new Student();
Person p = new Student();
Under these circumstances, an expression like
o.equals(p)
is valid. To decide which version of equals to use, Java uses the instance
variable o. The type of the variable o is Object but the type of object
that the variable o currently refers to is Student. Java uses the object’s
type (rather than the variable’s type) to determine which method to call.
As we said above, Java looks for a method with the appropriate signature
starting from the lowest class in the hierarchy of the object. In this case,
there is an equals method in the Student class so that is the one that
Java uses.
In the preceding discussion, we supposed that a version of the equals
method had been written for each of the classes Object, Person, and
Student. Suppose now that we define a method with the header
public void foo ()
only in the Student class. If we now write
Person p = new Student();
p.foo();
then the compiler produces an error. The problem here is similar to the
one that we encountered in the previous section; the compiler does not look
at previous lines of code so it is not sure what type p will have at execution
time. If it happens to have type Person at that time, that would be an
error because there is no foo method in the Person class. The compiler
does not want to take a chance on such an error occurring so it refuses to
compile the code. We can solve the problem in the same way that we did
previously — with a cast. If we write
Person p = new Student();
((Student)p).foo();
then the fragment will compile (and execute) correctly. The extra parenthe-
ses around (Student).p are necessary because a method call has a higher
precedence than a cast and we want the cast applied to p, not to the result
of evaluating p.foo().
7.3. INHERITANCE AND METHODS 287

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.

(a) Student s = new Student("Greg");


String name = s.getName();
(b) Person p = new Person("Roberto");
String number = ((Student)p).getNumber();
(c) Student s = new Student("Mona");
String name = ((Person)s).getName();
(d) Person p = new Student("Emilio");
String number = p.getNumber();
(e) Person p = new Student("Suzie");
String number = ((Student)p).getNumber();
(f) Student s = new Student("Gaby");
String number = ((Student)s).getNumber();
(g) Person p = new Student("Nicki");
String name = p.getName();

2. What is the output of the following program?2

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();
}
}

7.4 Using super and instanceof

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.

class Student extends Person


{
private String number;
public Student (String name, String number)
{
super(name);
this.number = number;
}
...
}

The statement super(name); is a call to the constructor in Person with


the String argument name. It will assign the appropriate value to the name
field of the Person part of a Student object.

Even if we do not use super in a constructor, it is still lurking quietly


in the background because every constructor that does not have a call to
the constructor of the superclass automatically has the statement super();
inserted as the first statement of the constructor. In this way, Java ensures
that in constructing an object, any inherited fields are initialized.
The use of super is not restricted to constructors; it can be used in
many ways. The next example shows how it can be used with toString
methods.
290 CHAPTER 7. OBJECT INTERACTION

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

public String toString ()


{
return "Name: " + name;
}

Since a Student is also a Person, we would probably want to have the


toString method of the Student class return both the name (defined in
the Person class) and the student number (defined in the Student class).
Since the name field of the Person class is private, we cannot have direct
access to it from the Student class but the toString method of the Person
class is public. We can get access to this method from the Student class
by using super.

public String toString ()


{
return super.toString() + " #: " + number;
}

Note in Example 2 that if, at some later stage, we decided to return


birth dates along with names, we could simply change the body of the
toString method in the Person class to
return "Name: " + name + " Birth date: " + birthDate;
and the change would be seen in both the Person class and the Student
class. No change would be required in the code of the toString method
in the Student class.
We can also use super to gain access to shadowed fields or overridden
methods in a superclass. Both of these ideas are illustrated in the next
example.
7.4. USING SUPER AND INSTANCEOF 291

Example 3
Suppose that the Dog class contained

class Dog
{
protected String name = "Dog";
public void feed ()
{
...

and the Samoyed class contained

class Samoyed extends Dog


{
private String name = "Samoyed";
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.

Since reference variables can refer to objects in subclasses of their type


and references can be altered during execution, we may not know the type
of object currently referred to by a reference variable. In such cases, we
can use the instanceof operator to help us.

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.

2. Suppose that a program contains the following classes.

class A
{
public A () {System.out.print("A");}
}
class B extends A
{
public B () {System.out.print("B");}
}

What, if anything, would be printed by each of the following state-


ments if they appeared in the main method of the program?

(a) A a = new A();


7.5. POLYMORPHISM AND THE ABSTRACT MODIFIER 293

(b) B b = new B();


(c) B c = new A();

7.5 Polymorphism and the abstract Modifier

Returning once more to our Object – Person – Student hierarchy, suppose


that we write
Person p1, p2;
p1 = new Person(..);
p2 = new Student(..);

If we then write

System.out.println(p1.toString());
System.out.println(p2.toString());

in both cases, p1 and p2 are references of type Person but, at execution


time, they refer to different types of objects (with different toString meth-
ods) This does not present a problem as Java decides at execution time
which method is appropriate and uses that one automatically. A situation
like this in which a different method is invoked depending on the type of
object associated with the call at runtime, is an example of a feature called
polymorphism, (Greek polu, many + morphē, form).
The last feature of class interaction that we will be looking at in
this chapter is the use of abstract methods. As an example to illustrate
this, suppose that we are writing a program to handle bank accounts. To
do so we might create a class called Account that has fields and meth-
ods required by all accounts. We could then define classes Savings and
Chequing that both extend Account. These classes would contain only the
necessary additional fields and methods that are unique to either savings
accounts or chequing accounts. If all account types give interest but dif-
ferent types of accounts have different rules for giving interest, a method
computeInterest should be defined in each of the subclasses. As the sys-
tem is developed, we may want to add other types of accounts. To ensure
that all types will have a method to compute interest, we can declare a
method computeInterest in the Account class but only define it in each
subclass. To do so, we make the declaration in the superclass with the
294 CHAPTER 7. OBJECT INTERACTION

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.

abstract class Account


{
abstract double computeInterest (int accountNumber);
...
}
class Savings extends Account
{
double computeInterest (int accountNumber)
{
// implementation here
...
}
...
}
class Chequing extends Account
{
double computeInterest (int accountNumber)
{
// implementation here
...
}
...
}

Although we can declare variables to have the type of an abstract class,


we cannot create objects of such a class. Only the non-abstract subclasses
of an abstract class can be instantiated. For a subclass of an abstract class
to be non-abstract, it must provide actual definitions for every abstract
7.5. POLYMORPHISM AND THE ABSTRACT MODIFIER 295

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();
}
}

3. In the previous question, if we had added the statement


a2.m2();
to the main method, the program would not compile.

(a) Explain this.


(b) How could the statement be modified so that the program would
compile and execute correctly?
(c) Without changing the statement, how could you use the abstract
modifier so that the statement would no longer cause an error?
(d) If you were to implement the change in part (c), what new error
would appear in the program?

7.6 Avoiding Errors and Debugging

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

classes in which the code appears. If this occurs in classes in the


same hierarchy, it can frequently be avoided by using super.
As an example, suppose that a class Toaster is a subclass of
Appliance. The toString method of the Toaster class might have
the form

public String toString ()


{
return super.toString()
+ " a toaster - capacity: " + maxSlices;
}

The toString method of Appliance would be responsible for return-


ing a string that is appropriate for all appliances. Only the informa-
tion specific to toasters would be added in the Toaster class.

2. Do not confuse method overriding (discussed in this chapter) with


method overloading (introduced in Chapter 5). We override a method
if we write another method, with the same signature as the original
method, in a subclass of the original class. On the other hand we
overload a method if we write more than one method with the same
identifier but different signatures. The fact that overriding requires
methods to have the same signature can cause errors. For example,
suppose we have a class

class Sample
{
public double foo (int n)
{
...
}
...
}

and the following subclass.

class Sub extends Sample


{
public double foo (double n)
{
298 CHAPTER 7. OBJECT INTERACTION

...
}
...
}

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

class Student extends Person


{
protected String name;
7.6. AVOIDING ERRORS AND DEBUGGING 299

protected int studentNumber;


...
}

then, if we create a Student object called Vincent, the identifier


Vincent.name will refer to the name field in the Student part of the
object, not the name field in the Person part. While it is valid, it is
probably not what we really intended.

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);
}
}

(a) What would be printed by this program?


(b) Is this an example of overriding or overloading?
(c) What would the program print if int were replaced by double?
(d) Explain your answer to part (c).
300 CHAPTER 7. OBJECT INTERACTION

7.7 Review Exercises 7

1. Draw a diagram showing the hierarchical relationship among the fol-


lowing classes: Bicycle, Car, Hatchback, Convertible, Truck, and
Vehicle.
2. Explain the meaning of the statement super(value); in a construc-
tor method.
3. Explain the difference between a private method and a protected
method.
4. What does it mean to have a method declared with the abstract
modifier?
5. Explain the difference between method overriding and method over-
loading.
6. State whether or not each fragment of code is valid. If it is not valid,
state why and note whether the error will be detected by Java during
compilation or during execution. Assume that the constructors are
valid.
(a) Thing t = new Thing();
Object o = t;
(b) Object o = new Object();
Thing t = o;
(c) Object o = new Object();
Thing t = (Thing)o;
(d) Object o = new Thing();
Thing t = o;
7. Suppose a program is to use the classes Book and History (extending
Book).
(a) In which class should each of the following fields be declared?
• title
• timePeriod
7.7. REVIEW EXERCISES 7 301

• 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

9. What would be printed by the following program?

public class Menagerie


{
public static void main(String[] args)
{
Pet p = new Cat("Gus");
p.feed();
p.stroke();
System.out.println(p);
Fish f = new Fish("Bubbles");
f.feed();
f.feed();
System.out.println(p);
System.out.println(f);
}
}

abstract class Pet


{
private String name;
protected boolean alive = true;
public Pet (String name)
{
this.name = name;
System.out.print("New pet: " + name);
}
public abstract void feed ();
public void stroke ()
{
System.out.println("mmm");
}
public String getStatus ()
{
if (alive)
return "alive";
else
return "dead";
}
public String toString()
7.7. REVIEW EXERCISES 7 303

{
return name;
}
}

class Cat extends Pet


{
public Cat (String name)
{
super(name);
System.out.println(" - a cat");
}
public void feed ()
{
System.out.println("meeow");
}
public void stroke ()
{
System.out.println("purr");
}
public String toString ()
{
return super.toString() + " is " + getStatus();
}
}

class Fish extends Pet


{
private int timesFed;
public Fish (String name)
{
super(name);
timesFed = 0;
System.out.println(" - a fish");
}
public void feed ()
{
System.out.println("bloop");
timesFed++;
if (timesFed > 1)
304 CHAPTER 7. OBJECT INTERACTION

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;
}
}

class Truck extends Vehicle


{
private int loadCapacity;
public Truck (int nw, int lc)
{
super(nw);
loadCapacity = lc;
}
public int getLoadCapacity ()
{
return loadCapacity;
}
}

class Car extends Vehicle


{
7.7. REVIEW EXERCISES 7 305

private int numSeats;


public Car (int ns)
{
super(4);
numSeats = ns;
}
public int getNumSeats ()
{
return numSeats;
}
}

Consider each of the following fragments to be in a main method


that uses these classes. All the constructors are valid. The other
statements fall into one of the following categories.
1. The statement will compile and execute correctly.
2. The statement will not compile but it can be repaired by a cast.
3. The statement will not compile and cannot be repaired by a
cast.
4. The statement will compile but fails to execute correctly.
For each fragment, identify the category to which the second state-
ment belongs.
(a) Vehicle v1 = new Vehicle(4);
int nw = v1.getNumWheels();
(b) Car c1 = new Car(6);
System.out.println(c1.getNumWheels());
(c) Vehicle v2 = new Car(4);
int capacity = v2.getNumSeats();
(d) Truck t1 = new Truck(18,14000);
int loadLimit = ((Vehicle)t1).getNumSeats();
(e) Car c2 = new Car(5);
int maxLoad = (((Truck)c2).getLoadCapacity());
(f) Vehicle v3 = new Vehicle(4);
System.out.println(((Car)v3).getNumSeats());
(g) Vehicle v4 = new Truck(4,1200);
System.out.println(((Truck)v4).getNumWheels());
306 CHAPTER 7. OBJECT INTERACTION

(h) Truck t2 = new Truck(10,8000);


Car c3 = (Car)t2;

Projects

11. Write a set of classes that could be used to do simple calculations on


geometric objects. The set should include the following classes.
(a) Shape, containing abstract methods area and perimeter
(b) Rectangle, a subclass of Shape, containing a constructor (with
parameters for length and width) along with methods area and
perimeter
(c) Triangle, a subclass of Shape, containing a constructor (with
parameters for the three sides of a triangle) along with methods
area (See page 84) and perimeter
(d) Square, a subclass of Rectangle, containing a constructor (with
a parameter for the length of a side) and methods that invoke
the methods of the Rectangle class to determine the perimeter
and area of a square.
Chapter 8

Arrays

Frequently, problems involve the manipulation of large


amounts of data – lists, tables, and so on. For many of
these problems, the array provides an appropriate struc-
ture for storing the data. In this chapter we examine
techniques for both creating and manipulating arrays in
Java.
308 CHAPTER 8. ARRAYS

8.1 Tables and 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

If we wanted to keep track of these data in a computer program, we


could use five variable identifiers: price1980, price1985, price1990,
price1995, and price2000. However, this is a bit cumbersome and, if
we wanted to extend our data to keep a record of the price every year or
even every month, then this solution would become totally unwieldy.
To overcome this problem, Java offers a very useful data structure, the
array — a collection of data items of the same type. An array to hold our
gasoline price data would require five elements. Java stores the elements
of an array in consecutive locations in memory. An array has a single
identifier with the elements being distinguished by an index. In Java, the
values of the indices (plural of index) always start at zero. If we were to
store our five gasoline prices in an array called price, then the values of
the indices would range from zero to four. We can picture the array as
shown in the following diagram. The identifiers of the individual elements
of the array are price[0], price[1], . . . , price[4].

0 1 2 3 4
price 0.27 0.51 0.59 0.56 0.72

In using arrays, it is important to distinguish between the elements of


the array and the indices. In the price array, the elements are the different
prices of gasoline while the indices mark the locations in the array of the
elements. The elements here are floating point values while the indices are
integers. It is also important to distinguish between the identifiers of the
elements and the values of the elements. For the price array, the identifiers
are price[0] and so on while the values are 0.27 and so on.
8.1. TABLES AND ARRAYS 309

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.

In Java, an array is a kind of object. To create an array, we proceed


in a way very similar to that used in creating any object. For example, to
create an array to hold the gasoline price data, we could begin with the
declaration
double[] price;
This creates the variable price and determines that the type of price
is a reference to an array of double values. As with other objects, the
declaration, by itself, does not actually create an array. To get Java to
allocate memory for an array of five double values, we can now write
price = new double[5];
Pictorially, the result of this statement is:

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

Suppose that the data in this table were to be represented by the


array mass declared by the statement
double[] mass = {0,1,4,6.9,9,10.8,12,14,16};
(a) What is the value of mass[2]?
(b) What is the value of mass[5]?
(c) What are the possible values of the indices of the array?
(d) What is the identifier of the element whose value is 6.9?
(e) Of what type are the elements?
(f) What is the value of mass.length?

8.2 Using Arrays

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;

In the example, we used a for statement to index through the array.


We could have used a while or a do but the for is simplest. It is almost
always the case that a for is the best way to index through an array. Notice
314 CHAPTER 8. ARRAYS

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

If we were now to write the assignment statement


p = q;
this would copy the value from the cell labelled q in the diagram to the one
labelled p. The effect of this would be to have p refer to the same array as
q. Since there is no longer any reference to the array of odd numbers, they
are no longer accessible to the program.

p q

-
- 0
1 3 5 7 9 2 4 6 8

Comparison of arrays is like comparison of other objects in that, if p


and q are array variables, the expression p == q will be true if and only if
2 See Appendix E for notes on exception handling.
8.2. USING ARRAYS 315

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

- 7.3 9.1 3.3 4.7 6.0

If we then write the statement


swap(masses,1,4);
the values at masses[1] and masses[4] will be switched.
masses

- 7.3 6.0 3.3 4.7 9.1

Exercises 8.2
1. What would be printed by the following program fragment?

int[] list = new int[4];


for (int i = 0; i < list.length; i++)
list[i] = 3 - i;
System.out.println(list[1]+2);
System.out.println(list[1+2]);
System.out.println(list[1]+list[2]);

2. Suppose that an array sample has been declared as follows:


int[] sample = new int[SIZE];
Write one or more statements to perform each task.
(a) Initialize all elements of the array to one.
8.3. MULTI-DIMENSIONAL ARRAYS 317

(b) Switch the values at either end of the array.


(c) Change any negative values to positive values (of the same mag-
nitude).
(d) Set the variable sampleSum to the sum of the values of all the
elements.
(e) Print the contents of the odd-numbered locations.

3. Write a method max that has one double array parameter. The
method should return the value of the largest element in the array.

4. Complete the definition of the method equals so that it returns true


if and only if its two array parameters contain equal elements.
public static boolean equals (double[] a, double[] b)

5. Write a program that repeatedly prompts the user to supply scores


(out of 10) on a test. The program should continue to ask the user
for marks until a negative value is supplied. Any values greater than
ten should be ignored. Once the program has read all the scores, it
should produce a table with the following headings:
Score # of Occurrences
The program should then calculate the mean score, rounded to one
decimal place.

8.3 Multi-Dimensional Arrays

Suppose that, because of your growing reputation as an expert with com-


puters, you have been asked to assist the local little theatre group in main-
taining records of seats sold for their upcoming play. The auditorium that
the group uses has twenty-five rows, each of which contain thirty seats. To
keep track of sales, it would be useful to have an array of boolean values in
which an element has the value true if and only if the seat has been sold.
Sales of seats in a single row could be monitored using a boolean array
of size thirty. This could be declared with the statement
boolean[] row = new boolean[30];
to produce the following result.
318 CHAPTER 8. ARRAYS

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

Just as for statements are usually used to index through a one-


dimensional array, nested for statements are usually used to index through
a two-dimensional array. For the array table shown in Example 1 we can
initialize the elements to one using the following fragment.
for (int row = 0; row < table.length; row++)
for (int col = 0; col < table[0].length; col++)
table[row][col] = 1;
Notice the way in which the upper bounds of the loops are written. The
number of rows in table is given by table.length while the number of
columns in row zero (and all other rows) is given by table[0].length.
Two-dimensional arrays can be initialized as they are being declared,
in a manner similar to that used for one-dimensional arrays.

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

We can do this with the following statement.


int[][] scores = {{5,2,7,3},
{6,8,7,9},
{4,5,2,3}};
On the right side of the assignment statement, the elements of each row
of the array are enclosed by brace brackets and separated by commas.
Similarly, the sets of values for each row are themselves enclosed by brace
brackets and separated by commas. The statement could have been written
8.3. MULTI-DIMENSIONAL ARRAYS 321

on one line but we wrote it the way that we did to make the structure of
the array clearer to a human reader.

The one-dimensional arrays that compose a two-dimensional array


need not all be of the same length, as the next example illustrates.

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.

double[][] triTable = new double[5][];


for (int row = 0; row < triTable.length; row++)
triTable[row] = new double[row+1];

We can picture the resulting array as follows:


columns
0 1 2 3 4
0 0
1 0 0
rows 2 0 0 0
3 0 0 0 0
4 0 0 0 0 0

Array declarations like the one for triTable in Example 3, in which


the second dimension’s size was unspecified, are legal. However, it is not
legal to have the first dimension’s size unspecified. For example, the dec-
laration
int[][] badArray = new int[][20];
is not permitted.
Non-rectangular two-dimensional arrays are sometimes called ragged
arrays. We can initialize ragged arrays in their declarations, just as we
did for rectangular arrays.
322 CHAPTER 8. ARRAYS

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

To process the elements of a ragged array, we must change our loop


structure slightly to take account of the fact that the length of each row
may be different.

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];

Multi-dimensional arrays are not limited to two dimensions. We can


create three-dimensional arrays, four-dimensional arrays, or arrays of even
higher dimensions. These higher-dimensional arrays are usually rectangu-
lar, but they need not be. We suggested that a two-dimensional rectangular
array could be visualized as a table with rows and columns. Similarly, a
rectangular three-dimensional array can be visualized as a set of tables,
8.3. MULTI-DIMENSIONAL ARRAYS 323

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

Finally, we note that, just as one-dimensional array references can


be used as parameters and return values of methods, so too can multi-
dimensional array references. Of course, the dimensions of arguments and
parameters must match and their types must be appropriate. It is also
possible to pass sub-arrays to methods, as the next example illustrates.

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

2. How many elements would there be in each of the arrays created by


the following declarations?
(a) double[][] first = new double[25][40];
8.3. MULTI-DIMENSIONAL ARRAYS 325

(b) boolean[][][] second = new boolean[3][6][50]


(c) char[][] third = new char[60][40];
(d) long[][][] fourth = new long[5][10][20];
3. Suppose that the following declarations have been made:

int a[][] = {{4,2,7},


{3,9,1}};
int i, j;

Determine what would be printed by each fragment.


(a) for (i = 0; i < a.length; i++)
{
for (j = 0; j < a[0].length; j++)
System.out.print(a[i][j]);
System.out.println();
}
(b) for (i = 0; i < a[0].length; i++)
{
for (j = 0; j < a.length; j++)
System.out.print(a[j][i]);
System.out.println();
}
(c) for (i = a.length - 1; i >= 0; i--)
{
for (j = 0; j < a[0].length; j++)
System.out.print(a[i][j]);
System.out.println();
}
4. For the array given in the previous question, write a fragment that
would print the elements of the array in the form

17
92
34

5. Write a method sum having one double[][] parameter. The method


should return the sum of the elements of the array passed to it. You
may assume that the array is rectangular.
326 CHAPTER 8. ARRAYS

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.

7. Write a method print that could be used to print a two-dimensional


ragged array of int values. Each row of elements should be printed
on its own line with one blank between each element.

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.

8.4 Arrays of Objects

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;
}

To create an array of Complex objects, we could proceed as follows. In our


main method, say, we could write
Complex[] points = new Complex[100];

This would create an array of 100 values (points[0], points[1], . . . ,


points[99]) of type reference to Complex. As with the declaration of any
array of references, each of these would be initialized to null. To actually
create 100 Complex objects, we could use the following loop.

for (int i = 0; i < points.length; i++)


points[i] = new Complex();

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:

String[] name = new String[STUDY_SIZE];


int[] age = new int[STUDY_SIZE];
double[] height = new double[STUDY_SIZE];
char[] sex = new char[STUDY_SIZE];

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
}

Now, to create a structure to store data on the children, we could write


Child[] patient = new Child[STUDY_SIZE];
The data for the first child in the study would now be called patient[0].
The age of this child would be patient[0].age.

There are a number of advantages to using an array of objects rather


than multiple arrays of data.
328 CHAPTER 8. ARRAYS

• 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;
}
...
}

class Square extends Shape


{
private double side;
public double area ()
{
return side*side;
}
...
}

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

3. Assuming that a Fraction class has been defined as shown, write


a fragment that could be used to create an array of 100 Fraction
objects representing the fractions
0 1 2 99
, , , . . .,
1 2 3 100
class Fraction
{
private int num;
private int den;

public Fraction (int n, int d)


{
num = n;
den = d;
}
}

4. Suppose that a program has an array shapes whose elements are of


type Shape, as described in Example 2. Write a fragment that will
print, for each element in the array, a message identifying the type of
shape (circle or square) represented by that object and its area.

8.5 Partially Filled 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).

To add a new element to such an array, we must update the current


size when an item is added. In addition, before adding a new item, we
must check that there is room in the array. Even though we set the array’s
length to be as large as we would ever need, it is almost certain that, if
a program is run often enough, all estimates of maxima and minima will
eventually be exceeded.
If an array is full when we try to add an element, we can solve that
problem by making the array larger. We cannot do this directly during
program execution since an array’s size is fixed at declaration. We can,
however, do it indirectly in the following way. We first create a new array
that is larger than the original, then copy all the elements of the original
array into the temporary one, and finally set the original array reference
to the new array.

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.)

list Before Insertion

list temp After Insertion

-
-

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

Following this analogy, to insert an item into a stack, we push it onto


the stack; to delete an item, we pop it from the stack.
To implement stacks in Java, we can use partially filled arrays.4 For
our stacks, we want to be able to perform the following operations:

• create create a new, empty stack

• push add a new item onto the top of the stack

• pop delete and return the item at the top of the stack

• isEmpty return true if and only if the stack is empty

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

public void push (Object o)


{
// insert object o into stack
if (stackSize == stack.length)
{
// array too small - double its size
Object[] temp = new Object [2*stack.length];
for (int i = 0; i < stack.length; i++)
temp[i] = stack[i];
stack = temp;
}
stack[stackSize] = o;
stackSize++;
}

public Object pop ()


{
// remove and return object at top of stack
if (stackSize == 0)
throw new RuntimeException
("Attempt to pop empty stack");
else
{
stackSize--;
return stack[stackSize];
}
}

public boolean isEmpty ()


{
// return true if and only if the stack is empty
return stackSize == 0;
}
}

Java actually supplies a Stack class in the package java.util with


methods for each of these operations plus some others. The implementation
used by Java is similar to that used here.
8.6. AVOIDING ERRORS AND DEBUGGING 335

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.

8.6 Avoiding Errors and Debugging

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

int negativeIndex = -1;


boolean noNegative = true;
for (int i = 0; i < list.length && noNegative; i++)
if (list[i] < 0)
{
negativeIndex = i;
noNegative = false;
}

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

1. In debugging a program that uses arrays, it is sometimes a good idea


to (temporarily) reduce the size of the arrays to very small values
until the error can be found and eliminated.
2. To trace the activity in an array, print both the components of the
array and the associated index values. For example, to see what is
occurring in an array called list, we could write
for (int i = 0; i < list.length; i++)
System.out.println("list[" + i + "] = " + list[i]);
In this way, you can see not only what the values are but where they
are located.
8.6. AVOIDING ERRORS AND DEBUGGING 337

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

4. This problem of forgetting to initialize arrays is very common with


arrays of objects. As an example, the fragment
Object[] list = new Object[10];
System.out.println(list[0].equals(list[1]);
will compile without warning but will cause Java to throw a Null-
PointerException when it attempts execution. The problem is that
new here creates an array of null references. An attempt to use the
instance method equals when list[0] is not referring to an instance
of an object naturally causes Java to get upset.

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.

double largest = list[0];


int i = 1;
while (i < list.length)
if (list[i++] > largest)
largest = list[i-1];
338 CHAPTER 8. ARRAYS

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

3. Identify and correct the error in each declaration.

(a) double a = new double[10];


(b) int[] b = new int[];
(c) char[][] c = char[30][];
(d) float[][] d = new float[][10];
(e) int[] e = new {3,5,2,9,1};

4. A programmer, using a square two-dimensional array of int values


called table, wanted to sum the elements along the main diagonal
(the diagonal whose elements are table[0][0], table[1][1], and
so on). To do this the programmer wrote

int total = 0;
for (int i = 0; i < list.length; i++)
for (int j = 0; j < list.length; j++)
total += table[i][j];

(a) What does the fragment actually do?


(b) Write a fragment that does set total to the sum of the elements
of the main diagonal.
(c) Write a fragment that sets total to the sum of the elements of
the other diagonal of table.

8.7 Review Exercises 8

1. Does the statement double[] list; create an array of double ele-


ments? Explain.
8.7. REVIEW EXERCISES 8 339

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)

The values a0 , a1 , . . . , an are called the coefficients of the polynomial.


Complete the definition of the method eval so that it returns the
value at x of a polynomial whose coefficients are stored in the array a.
public static double eval (double[] a, double x)
340 CHAPTER 8. ARRAYS

7. Complete the definition of the method randomize whose header is


public static int[] randomize (int n)
The method should return an array of size n whose elements are the
values 0 . . . n − 1 ordered randomly. As an example, randomize(5)
might return [4, 2, 0, 3, 1].
8. Lacsap’s Triangle is a triangular array of numbers in which each row
starts and ends with the row number and each interior value is the
sum of the two values on either side of it in the preceding row. The
following diagram shows the first five rows of Lacsap’s Triangle.

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

11. The Sieve of Eratosthenes is the name given to an algorithm5 for


finding prime numbers (natural numbers having exactly two distinct
natural number divisors, one and themselves). The sieve starts by
considering all numbers greater than one as possible primes.

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
\ ...

In the algorithm, this process continues (eliminating multiples of five,


seven, and so on) until it is no longer possible to eliminate any mul-
tiples of values left in the table. The values that remain must all be
primes.
Write a program to implement Eratosthene’s Sieve. The program
should ask the user for an upper bound and should then print all the
prime numbers less than or equal to that upper bound. In your
program, use a boolean array to maintain a record of the numbers
that are possible primes.
The output should have eight values per line with each value
right-justified in a field that is ten characters wide. You may find the
methods of the Out class shown in Appendix A useful for this.
5 An algorithm is a set of instructions for performing some process in a finite number
of steps.
342 CHAPTER 8. ARRAYS

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:

• King (K): can move one space in any direction


• Queen (Q): can move any number of spaces in any direction
• Rook (R): can move any number of spaces, either horizontally
or vertically
• Bishop (B): can move any number of spaces diagonally
• Knight (N): can move two spaces vertically followed by one space
horizontally or two spaces horizontally followed by one space
vertically

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

The output from the program should consist of a visual repre-


sentation of the chess board, as shown in the example. The diagram
should show the pieces in their positions, the position under attack
(denoted by the letter X) and dots in empty positions. The diagram
should be followed by a list of the pieces that can take a piece at the
location marked by the X. A piece is indicated by its code, its column,
and its row, in that order. The attacking pieces should be listed in
the following order: from the bottom row to the top row and from
left to right within a row. To determine whether or not a piece is
able to attack a position, ignore any other pieces that may be in the
way.
Sample input for the program is shown here. (Although all input is
shown on one line, when the program runs, each character should be
on one line.)
Q b 2 R h 4 K e 4 B c 4 X d 4
For such input, the program should produce the following output.
........
........
........
........
..BXK..R
........
.Q......
........
Qb2
Ke4
Rh4
14. Write a program that permits two players to play the game of Adja-
cency. The game is played using X’s and O’s on an 8×8 board, initially
set up as shown in the following diagram in which dots indicate empty
squares. The rows and columns are numbered as shown.
8 ......OO
7 ......OO
6 ........
5 ........
4 ........
3 ........
2 XX......
1 XX......
12345678
344 CHAPTER 8. ARRAYS

The first player begins by placing an X on an empty square. All


O’s that are on adjacent squares are then replaced with X’s. Two
squares are considered to be adjacent if they are one square apart
either horizontally, vertically, or diagonally. To illustrate, if the first
player puts an X on the square with coordinates (6, 7), column 6 and
row 7, the resulting board configuration would be:

8 ......XO
7 .....XXO
6 ........
5 ........
4 ........
3 ........
2 XX......
1 XX......
12345678

Play continues with the second player placing an O on an empty


square. The two players then take alternate turns. The game can
be terminated in one of two ways. The players may agree before the
first turn to terminate the game after a specific number of rounds or
play may continue until the board is filled. At the end of the game,
the player with the most pieces is the winner.
Your program should begin by asking for the names of the play-
ers, determining the way in which they want the game to terminate,
and displaying the initial board. It should then repeatedly prompt
the players for their moves, displaying the updated board after each
move. If a player supplies invalid coordinates for a move, the program
should spot this and ask the player to re-submit valid coordinates
immediately. At the end of the game, the program should announce
either the name of the winner, if there is one, or print a message in
case of a tie.
Real Jobs — Real People
Name: Geoffrey Cann
Title: Partner
Company: Deloitte Consulting
Education: B.Comm. Management Information Systems,
McGill University (1984)
M.B.A., University of Western Ontario (1989)

Q: Please describe a bit about your career history.


A: While I was an undergraduate, I worked as a subcontractor to small software
development companies building applications for a new IBM technology
called the “Personal Computer”. After that, I worked for Imperial Oil as
a programmer building financial and marketing systems in COBOL and
other mainframe languages.
After completing my MBA, I joined Deloitte Consulting where I was
initially involved in writing technical specifications for word processing,
procurement, email, and other systems. Then I got into developing infor-
mation technology (IT) strategies and helping organizations build custom-
made systems for core functions.
In 1993, I relocated to Hong Kong to work with a small Deloitte Con-
sulting team to develop a consulting practice in the region. I did a lot of
systems work for clients in Hong Kong and mainland China. In 1995, I
moved to Western Canada and focused on evaluation and selection of large
enterprise resource planning systems like SAP and PeopleSoft.
Since 1999, I have been working primarily with a large Canadian en-
ergy company. It is going through a major transformation and I am helping
with all aspects of its business plans and information systems.
Q: What are some of the key IT challenges facing organizations you work with?
A: One of the biggest challenges for many organizations is the rapid pace of
change. Technology changes too quickly for most companies to be able to
embrace and implement the latest advancements. Since it is so difficult to
keep up, companies are often one generation behind both from a systems
and a business model perspective.
Another major challenge organizations face is dependence on an aging
group of programmers who built many of their core systems. As these
individuals retire they often leave huge gaps in knowledge.
A third challenge facing many companies is the emergence of third
world economies with outstanding software development capabilities. Com-

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

We have already encountered strings many times and


have learned how to read, write, and compare string
values. We now turn to a more general study of Java’s
strings. In this chapter, we examine many of the
methods from both the String and StringTokenizer
classes.
348 CHAPTER 9. STRINGS

9.1 String Objects


As we have noted previously, strings in Java are objects — instances of the
class String (in the package java.lang). A variable declared to be of type
String is a reference to a String object. We can create and initialize a
string using a constructor, just as we have done for other objects.

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.

It is appropriate to use a constructor explicitly in cases where we want


to produce a String object with the same value as a previously defined
String object.

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

It is possible for a variable of type String to be in a state in which it


does not refer to a string containing characters. This can occur in a number
of ways and it is important to be clear about the differences among them.
One way that this can happen arises if we write
String s1;
The result of this is an uninitialized string. An attempt to use s1 in this
state will result in an error during compilation of the program. To avoid
such compilation errors, we can initialize a string variable so that it does
not refer to a string (but is still defined) by assigning it the value null. If
we write
String s2 = null;
then we can print the string (producing the output “null”) or compare
the string to other strings or compare the string to the value null. As a
third possibility, a string can be assigned the value of what is sometimes
called the empty string, a string containing no characters. We can do this
by writing
String s3 = "";
This creates a string object whose length is zero. If we print the value of
s3, it produces no output. The variables s1, s2, and s3 are illustrated in
the next diagram. As we have done with other reference variables, we have
used a dot inside s1 to indicate that it has no value and the ground symbol
with s2 to denote a null reference.

s1 s2 s3
.

- ""

The class String contains no mutator methods; once we create a


string, its value cannot be changed. Because of this, we say that strings
are immutable objects.1 This does not mean that we cannot change values
of string variables.

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"

Although the string containing "Hello" still exists, it is no longer accessi-


ble.

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 + "|");

3. What would be printed by each fragment?


9.2. STRING METHODS 351

(a) String s = "Sample";


String t = s;
s = "Simple";
System.out.println("s is: " + s);
System.out.println("t is: " + t);
(b) String s = "First";
String t = new String(s);
s = "Second";
System.out.println("s is: " + s);
System.out.println("t is: " + t);
4. (a) What do we mean when we say that strings are immutable?
(b) Is the following fragment legal? Explain.
String s = "old";
s = "new";

9.2 String Methods

Although concatenation is the only operator that can be applied to String


objects, Java has many methods in the String class that allow us to ma-
nipulate text quite easily. In Chapter 3, we examined the methods equals
and compareTo that can be used for comparing and ordering strings. In
this section, we extend our study of string methods with an examination
of many of the more commonly used ones. Our study will not be complete;
if you are going to be doing a lot of work with strings, it would probably
be a good idea to take a close look at the full set of methods available in
the class.
length
In some ways, strings in Java are much like arrays. For example, both
arrays and strings have lengths associated with them. The concepts are
not, however, identical. For an array, its length is a property; the array a
has a length given by a.length. On the other hand, the length of a string
is given by the instance method length in the String class that returns
the number of characters in its implicit object; the string s has a length
given by the value of s.length().
352 CHAPTER 9. STRINGS

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’

(b) s.charAt(5) will return ’r’

(c) s.charAt(6) will throw a StringIndexOutOfBoundsException

2 See Appendix E for notes on exception handling.


9.2. STRING METHODS 353

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.

public static void printLocations (char c, String s)


{
for (int i = 0; i < s.length(); i++)
if (s.charAt(i) == c)
System.out.println(i);
}

public static void printLocations (char c, String s)


{
final int NOT_FOUND = -1;
int next = s.indexOf(c);
while (next != NOT_FOUND)
{
System.out.println(next);
next = s.indexOf(c,next+1);
}
}

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

The method also exists in a two-parameter form with the header


public String substring (int start, int pastEnd)
This form returns a substring of its implicit object from the index specified
by start to the character before the index specified by pastEnd. Although
this may appear at first to be a bit strange, it turns out to be a useful and
convenient form. For one thing, in this form the length of the substring is
specified simply by the difference between the two parameters.

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

"Lots of extra blanks"


with no leading or trailing white space. Notice that the method does not
alter interior blanks; the extra blanks between words in s are still in the
string returned by the method.

To show these methods in context, let us construct a method that


removes all excess blanks in a string.

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

toUpperCase toLowerCase equalsIgnoreCase


Recall that, in comparing strings, upper case letters and lower case let-
ters are considered to be different. If we want to compare strings containing
letters, we can avoid problems involving the case of letters in a number of
ways. One possibility is to create a string that is equal to a given string
but with all letters converted to upper case (and all other characters left
unchanged) by using the method toUpperCase from the String class. The
method has header
public String toUpperCase()
As you might expect, there is also a method called toLowerCase in the
String class whose form and behaviour are exactly analogous to that of
toUpperCase. As usual, since strings are immutable, neither of these meth-
ods alters its implicit string object. Instead, they return new string objects
that may contain new characters.
One other solution to the problems produced by different cases is to
do comparisons with the method equalsIgnoreCase which has header
boolean equalsIgnoreCase (String other)
Its effect is exactly the same as that of equals but, as the name suggests,
upper and lower case characters will be considered equal.

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"

Java allows us to chain instance methods in order to produce com-


pound effects. This can be very effective in operations involving strings.
Given an expression of the form
<object>.<method1 >.<method2 >
Java will first execute <method1 > using <object> as its implicit object
parameter. It will then execute <method2 > using the value returned by
the first call as its implicit object.

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) s.charAt(3) (b) s.substring(8)


(c) s.length() (d) s.indexOf(’a’)
(e) s.charAt(0) (f) s.substring(1,4)
(g) s.substring(1) (h) s.indexOf(’m’,4)
(i) s.charAt(4) (j) s.substring(4,5)

2. State the value of each expression.

(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()

4. Complete the definition of the method count so that it returns the


number of occurrences of the character c in the string s.
public static int count (char c, String s)

5. Complete the definition of the method replace so that it returns a


string in which all occurrences of oldChar in the string s are replaced
by newChar.
public static String replace (String s, char oldChar,
char newChar)

6. Write a method called averageLength that has one String param-


eter, s. The method should return, as a double value, the average
length of the words in s. Assume that s consists only of words sepa-
rated by single blanks, without any leading or trailing blanks.
360 CHAPTER 9. STRINGS

7. A hetero-literal word pair is defined as a pair of words having no


letters in common.

As examples, "TERRY" and "FOX" form a hetero-literal word pair


"WAYNE" and "GRETZKY" do not
(because of the ’E’ and ’Y’ in each word).

Write a method called isHeteroPair that has two String parame-


ters, wordA and wordB. The method should return a boolean value:
true if wordA and wordB form a hetero-literal word pair and false
if they do not. You may assume that all characters are uppercase
letters.

9.3 Arrays of Strings

Like any other objects, strings can be elements of arrays. To create an


array of strings holding the names of the seasons of the year, we can start
by making the declaration
String[] seasons = new String[4];
This creates an array whose four elements are references to objects of type
String. Since they are reference types, they will be initialized to null
giving the structure illustrated in the next diagram.
seasons

The values of the seasons could then be assigned by writing


season[0] = "Spring";
season[1] = "Summer";
season[2] = "Fall";
season[3] = "Winter";
The results of this are displayed in the next diagram.
9.3. ARRAYS OF STRINGS 361

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

java Update 2010 12 31


Inside the program, we could convert the arguments to integers by writing
int year = Integer.parseInt(args[0]);
int month = Integer.parseInt(args[1]);
int day = Integer.parseInt(args[2]);

Parsing methods are available for converting strings to any of Java’s


numerical types. For example, to convert a string to a double value, we
could use the method parseDouble in the Double class. A complete list of
these methods is given in the table on page 588.
If a program is expecting a particular form of command line argument,
then it is easy to check to see whether or not the user has supplied the
appropriate number of values.

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.

public static void main (String[] args)


{
if (args.length != 3)
{
System.out.println("Program requires 3 arguments.");
System.exit(1);
}
else
// rest of main method

In Example 3, the statement System.exit(1); is used to terminate


the program. The exit method of the System class causes a program
to stop immediately. An argument of zero is passed to exit on normal
termination while some other value (one in our example) is used to indicate
abnormal termination.
364 CHAPTER 9. STRINGS

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)

2. Complete the definition of the method first so that it returns the


string that would appear first if the strings of the parameter were
placed in lexicographic order.
public static String first (String[] list)

3. Write a fragment that will print the values of a program’s command


line arguments. They should be printed on one line, separated by
commas followed by single blanks. If there are no arguments, an
appropriate message should be printed.

4. A program is expecting command line arguments consisting of a mi-


nus sign followed by any number of lower case letters. For example,
-ad would be a valid argument. Write a fragment that checks the
form of the argument. If there is no argument or if the argument is
of the wrong form, the fragment should print an appropriate message
and cause the program to terminate.

5. A simple calculator program is expecting command line arguments


that have the form <operand1 > <operator> <operand2 > where both
<operand1 > and <operand2 > are integers while <operator> is one
of +, -, *, /, or %. Write a method that will calculate and return
the value of <operand1 > <operator> <operand2 >. For example, if
the command line arguments are 5 + 3, the method should return
the value 8. Assume that the command line argument values are all
valid.

9.4 The StringTokenizer Class

As we have already mentioned, Java’s Application Programming Interface


(API) is a collection of hundreds of classes designed to add power and
flexibility to the language. The StringTokenizer class is one of these; it
9.4. THE STRINGTOKENIZER CLASS 365

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

as delimiters. It also uses any other white space characters (such as


newlines, tabs, carriage returns, or form feeds) as delimiters.

• public StringTokenizer (String s, String d)


This constructs a StringTokenizer object for the string s using any
of the characters in the string d as delimiters. The characters in d
are not treated as tokens.

• public StringTokenizer (String s, String d, boolean flag)


Like the previous constructor, this one constructs a StringTokenizer
object for the string s using any of the characters in the string d as
delimiters. Now, however, if the value of flag is true, then the
delimiter characters are also returned as tokens. (Each delimiter is
returned as a string of length one.) If flag is false, the construc-
tor behaves like the second one with characters in d not treated as
tokens.

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"
")"

(b) The statement


StringTokenizer st2 = new StringTokenizer
("http://www.java.sun.com",":/.");
9.4. THE STRINGTOKENIZER CLASS 367

would create a StringTokenizer object st2 with tokens


"http"
"www"
"java"
"sun"
"com"

(c) The statement


StringTokenizer st3 = new StringTokenizer
(s," \t\n\r\f",false);
would have the same effect as writing
StringTokenizer st3 = new StringTokenizer(s);
because the delimiters specified in the first constructor are the default
whitespace delimiters and the fact that the flag in the first construc-
tor is set to false means that the delimiters themselves will not be
returned as tokens.

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

1. Assuming that the string s contains sentences that consist of words,


blanks, and punctuation marks (period, comma, exclamation mark,
question mark, colon, and semi-colon), construct a StringTokenizer
object whose tokens consist only of the words in s.

2. Write a class method wordCount that has a single String parameter,


s. Assuming that s consists of words separated by whitespace, the
method should return the number of words in s.

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

9.5 Avoiding Errors and Debugging

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.

2. Remember that comparisons of strings, like comparisons of any ob-


jects, should not be made using the == operator. As we have said
before, if s1 and s2 are references to strings, then s1 == s2 will be
equal only if s1 and s2 refer to the same object, not if they refer to
equal objects stored at different locations. Always use either equals
or compareTo when comparing strings.

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

upper case. The assignment then changes the reference s so that it


now refers to the newly created string. The original string to which
s referred is no longer available (unless some other variable refers to
it).
4. Do not confuse characters with strings that are one character long.
These are very different creatures. If, for example, we were to write
char c = ’*’;
String s = "*";
then we can illustrate the results as follows:

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);

5. The operator +, as we have seen, is overloaded so that, when either


operand is a string, it means concatenation rather than addition.
Attempting to join two characters using + to produce a string does
not have the desired effect. As an example, the statement
System.out.println(’x’ + ’y’);
will not print xy. The actual output is 241!
The reason for this is that Java interprets + in this case to mean
addition (because no strings are involved). As we saw in Chapter 2,
we can do arithmetic involving characters with Java using the Uni-
code encoding values of the characters in the arithmetic operations.
As you can verify from the table on page 600, the Unicode values of
’x’ and ’y’ are 120 and 121 respectively. The sum of these values
is 241.
9.5. AVOIDING ERRORS AND DEBUGGING 371

We can force Java to concatenate characters to form strings in a


number of ways. For our example, the simplest way to achieve this
is to write the argument of the println command as
"" + ’x’ + ’y’
By placing an empty string at the beginning of the expression, we
make Java interpret the first + to mean concatenation. Evaluating
the expression from left to right, Java would obtain the following
result:
"" + ’x’ + ’y’ ⇒ "x" + ’y’ ⇒ "xy"

6. If you want to create a string that replaces one character in a string


by some other character, you cannot use the charAt method to do so.
As an example, if we wish to replace the character at index 4 in the
string s by an asterisk, it is a mistake to attempt to write something
like
s.charAt(4) = ’*’; // wrong!
The problem here is that the left side of the assignment statement
contains a method call that returns a char value rather than the
identifier of a location in which a char value can be stored. A correct
way to achieve the desired effect is to write
s = s.substring(0,4) + ’*’ + s.substring(5);

7. Although strings and arrays of characters have many things in com-


mon, they are not the same and they must be treated appropriately.
For example, if s is a string and a is an array of characters, then,

• the first character of the string is s.charAt(0) while the first


character of the array is a[0]
• the length of the string is s.length() while the length of the
array is a.length

Debugging

1. In tracing programs involving strings, it is a good idea to print strings


with some delimiter on either side of them. For example, to determine
the value of a string s, one might write the string
"|" + s + "|"
This has a couple of purposes:
372 CHAPTER 9. STRINGS

• If s is empty, you will at least print something — two adjacent


vertical bars.
• If s contains leading or trailing blanks, they will appear between
the bars and the non-blank characters, allowing you to see them.
2. The two-parameter version of the substring method that has the
header
String substring (int start, int pastEnd)
is a frequent source of problems because the second parameter spec-
ifies the index after the last character of the substring rather than
the last character itself. If you work with the substring method long
enough, you may begin to realize that the way that the method is
designed is, in fact, a good idea but it does tend to cause grief to
beginning programmers.

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()

2. Given the declaration


String [] names = new String [10];
state, with justification, the result of each statement.
(a) System.out.println(names[0]);
(b) System.out.println(names.length);
(c) System.out.println(names.length());
(d) System.out.println(names[0].length());
9.6. REVIEW EXERCISES 9 373

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.

9.6 Review Exercises 9


1. What would be printed by the following fragment?

String s = "one";
String t = new String(s);
String u = s;
u = "two";
t = "three";
System.out.println(s + " " + t + " " + u);

2. Given the declaration


String s = "Alyssa Titus";
find the value of each expression.
(a) s.charAt(3) (b) s.indexOf(’a’)
(c) s.substring(7) (d) s.length()
(e) s.substring(2,3) (f) s.charAt(2)
374 CHAPTER 9. STRINGS

(g) s.indexOf(’o’) (h) s.indexOf(’s’,5)


(i) s.toLowerCase() (j) s.substring(1,7)
(k) s.equals("Alyssa") (l) s.compareTo("a") < 0

3. For each expression, state whether it is true or false.

(a) ’3’ > ’2’


(b) ’m’ < ’M’
(c) ’7’ < ’s’
(d) ’z’ < ’ ’
(e) "foo".compareTo("fool") < 0
(f) "foo".compareTo("Foo") > 0

4. A program is expecting command line arguments of the form


<given name> <family name> <age> <sex>
where <sex> is a single character. For example,
Luka Matutinović 19 m
would be a valid sequence of command line arguments.
Assuming that the main method of the program has the form
public static void main (String[] args)
write a fragment that would use these arguments to assign appropri-
ate values to the String variable fullName, the int variable age,
and the char variable sex. For the example, the fragment should set
fullName = "Luka Matutinović"
age = 19
sex = ’m’
Assume that all arguments are valid.

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

private String name;


private String breed;
private int age;

(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

(b) Write a toString method for the class. As an example, for an


object representing a three year old Samoyed named “Toby”,
the method should return "Toby - Breed: Samoyed, Age: 3"

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

The program should work for a string up to ten characters long. If a


user supplies a string that is longer than ten characters, the program
should use only the first ten characters in forming the pattern.

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”.

(a) Write a boolean-valued method isPalindrome that has a single


String parameter. The method should return true if and only
if its parameter is a palindrome.
(b) Modify your method so that it ignores cases of letters, punctu-
ation marks, and blanks in making a decision about whether or
not a string is a palindrome. For example, the string:
"A man, a plan, a canal: Panama!"
(a reference to Ferdinand de Lessups, the chief engineer of the
first attempt to build the Panama Canal) should be taken to be
a palindrome.
376 CHAPTER 9. STRINGS

8. In the Review Exercises of Chapter 4, Question 13 asked for a pro-


gram that implemented a test for divisibility by 11. In answering that
question, you were to assume that the input could be represented by
a long value. If the input is read as a String, this restriction is
no longer necessary. Write a program that repeatedly prompts the
user for arbitrarily long integers and uses Dodgson’s algorithm to
determine whether or not each one is divisible by 11.

Projects

9. Write a program that reads names in standard form and prints them
in the form

<last name>, <any given initials>

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

be written more than three times in a row. To avoid this, the


system uses IV = 4, IX = 9, XL = 40, XC = 90, CD = 400, and
CM = 900.
As examples, VIII = 8, XXIV = 24, LXXXIX = 89, MMVIII = 2008,
MDCCCLXXIX = 1879
11. Write a program that will perform arithmetic operations on pairs
of integers written in various number bases. Input to the program
should consist of an arbitrary number of expressions, each of which is
written on one line. Each expression should consist of the following
five items separated by one or more blanks:
• an input base: 2 to 10
• a first integer, written in the input base
• an operator: +, -, *, /, %
• a second integer, written in the input base
• an output base: 2 to 10
The two integers written in the input base should be converted to
a base ten representation, the indicated arithmetic operation should
be performed, the result should be converted to the output base, and
a summary should be printed in a tasteful manner. As an example,
input of
8 73 + 216 5
should produce output something like the following:
73 (base 8) + 216 (base 8)
= 59 + 142
= 201
= 1301 (base 5)
Your program should be modular, using methods appropriately. Bad
data should be rejected by your program without causing it great
grief.
12. One of the tasks performed by a text editor or word processor is the
breaking up of text into lines of a specified length. For this assignment
you are to write a program that carries out this task. Specifically,
your program should perform the following tasks:
• Prompt the user, repeatedly if necessary, for a line width: an
integer that is at least 20 and at most 60.
378 CHAPTER 9. STRINGS

• Read lines of text and rearrange the text if necessary so that


the words fit into lines of the specified width. The input will be
terminated by a single line containing only ZZZ. You should not
make any assumptions about the maximum length of a line of
input or the maximum number of words in a line of input. You
may assume that there will be no more than 100 lines of output.
If a single word is longer than a full output line, display it on
a line by itself with blank lines preceding and following the line
containing the word. As illustrated in the following example,
there may be more than one blank between words. It is also
possible that the input may contain blanks before punctuation
marks. These should be eliminated. For example, the text
Bubbles , baubles , and bangles are beautiful .
should be written as:
Bubbles, baubles, and bangles are beautiful.
You may assume that the input contains only words and the fol-
lowing six punctuation marks: period(.), comma(,), colon (:),
semi-colon (;), exclamation mark (!), and question mark (?). If
a punctuation mark occurs after a word whose length is equal to
or greater than the line length, that punctuation mark should
be printed immediately following the word. In such cases, the
word should be preceded and followed by blank lines.
• Print the re-formatted text. This output should not include the
ZZZ that terminated the input.
The StringTokenizer class, described in Section 9.4 of the text,
contains methods that you may find helpful in writing your program.
It is not necessary to use these methods; the project can be done
using only methods from the String class.
Your program should behave as shown in the following example.

Provide a line width (20-60): 75


Provide a line width (20-60): 35
Provide input (ZZZ to stop):
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.
9.6. REVIEW EXERCISES 9 379

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.

The last line

13. Extend the program of the previous question so that it produces


text that is right-justified. In this form, extra blanks are inserted if
necessary between words so that each line of the resulting text has
exactly the width that was specified. Blanks should be distributed in
a line so that the inter-word spaces are as close to equal as possible. If
some inter-word spaces are larger than others, the larger ones should
be on the left. For example, if ten blanks must be distributed over
four inter-word spaces, they should be distributed left to right in the
order 3-3-2-2. You should not try to justify the text of a line if there
is only one word on a line or if the line is the last line of the text. For
the example of the previous question, your program should produce
output of the following form:

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.

The last line


Chapter 10

Searching and Sorting

Accessing of data is done by a process called searching.


Searching can be made more efficient by organizing the
data in some way. This is often done by sorting the data:
arranging items in some order. Searching and sorting
are the primary activities in many computer facilities.
In fact, these topics are so important that entire books
have been devoted to them. To choose a searching or
sorting method that is most appropriate for a given set
of data is often a difficult problem, one that depends on
the amount and nature of the data and the equipment on
which the processing is to be performed. In this chapter
we begin to probe these ideas by examining a number of
different searching and sorting techniques and looking
at some criteria for deciding which to choose in a given
set of circumstances. Throughout this chapter, most
examples and questions involve either items of Java’s
primitive types or strings. This simplifies the examples
but the resulting methods are of limited use. At the end
of the chapter, we modify our techniques so that they
can be used in more general ways.
382 CHAPTER 10. SEARCHING AND SORTING

10.1 Sequential Search

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.

Although this method works, it is not as efficient as we might wish.


Even if it finds item early in the search, it stupidly goes on looking through
the rest of the array. We can correct this defect by using a boolean variable
that acts as a flag to stop the search as soon as item has been found. We
use this device in the next example.
10.1. SEQUENTIAL SEARCH 383

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;
}

We can achieve even greater efficiency for our sequential search, as


shown in the next example.

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

10.2 Binary Search

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

One final probe, looking at 47, produces a successful search. In only


four comparisons, we have been able to find the item that we want.
Let us now see how we can implement a binary search in Java. Suppose
that we are searching for item in an array called list. At the start of the
search, item could be anywhere in list but, as the search proceeds, the
interval that is being searched is reduced over and over again. To keep
track of the bounds of this interval, we use two int variables: bottom,
containing the index of the lower bound of the current interval and top,
containing the index of the upper bound. The easiest way to find the index
of the middle (or near middle) of the interval is to take the average of
bottom and top. Once we have found the middle, we examine the value
located there. If it is equal to item, the value we are seeking, we are done.
If it is smaller than item, we know that we can discard both the value at
middle and all values below this. To do so, we simply change the value of
bottom to middle + 1. Similarly, if the value located at middle is larger
than item we discard the upper values of the interval by setting top to
middle - 1.

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

The middle of the remaining interval is (4 + 6)/2 = 5, as shown in the


next diagram.

bottom middle top


4 5 6

? ? ?
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.

bottom middle top


4 4 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

public static int binSearch (double[] list, double item)


{
int bottom = 0; // lower bound of subarray
int top = list.length - 1; // upper bound of subarray
int middle; // middle of subarray
boolean found = false; // to stop if item found
int location = -1; // index of item in array

while (bottom <= top && !found)


{
middle = (bottom + top)/2;
if (list[middle] == item) // success
{
found = true;
location = middle;
}
else if (list[middle] < item) // not in bottom half
bottom = middle + 1;
else // item cannot be in top half
top = middle - 1;
}
return location;
}

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

Trace the execution of the method binSearch shown in this section


as it searches for the following values of item. In each trace, show
10.3. INSERTION SORT 389

the progress of the search by using diagrams similar to those in Ex-


ample 1.
(a) 72 (b) 41 (c) 62
2. What changes would have to be made to binSearch so that it will
search an array that is sorted in descending order.
3. Rewrite binSearch so that, if a search is unsuccessful, the method
will return the index of the value nearest to item, instead of return-
ing −1. (If there is a tie, return the smaller index.)
4. What is the maximum number of comparisons that might be neces-
sary to perform a binary search on a list containing seven items?
5. Repeat the previous question for lists with the indicated sizes.
(a) 3 (b) 15 (c) 31 (d) 63
(e) 100 (f) 500 (g) 1 000 (h) 10 000

10.3 Insertion Sort

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

To implement this algorithm for an array of values, we must examine


and correctly insert each of the values in the array from the second position
up to the last one. This requires a loop like the following, for an array called
list.
10.3. INSERTION SORT 391

for (int top = 1; top < list.length; top++)


// insert element at top into its correct position
// among the elements from 0 to top - 1
At each stage or pass of the sort, we first copy the element that we want to
insert into a temporary location. We then move from right to left through
the (already sorted) items on the left of the value that we are inserting. If a
value in the list is larger than the new one, it is moved one space to the right
(to provide room for the new item). Once the correct location of the new
value is found, it is then inserted back into the array from the temporary
location in which it had been saved. These ideas are incorporated into the
method of the next example.

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.

10.4 Selection Sort

In using an insertion sort, values are constantly being moved. A sorting


technique that reduces the amount of data movement is the selection sort.
Here, we again perform a number of passes through the data and, after each
pass, we again place one more item into an ordered sequence. Now, how-
ever, once an item has been placed in its position in the ordered sequence,
it is never moved again.
We can begin a selection sort by scanning all the values, finding the
largest one, and placing it at the top of the list, exchanging it with the
item that was originally in this position.
10.4. SELECTION SORT 393

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

for (int top = list.length - 1; top > 0; top--)


// locate largest item and then
// swap it with item at list[top]
We have already seen how to find the largest element in an array and how
to swap elements in an array. The next example shows the selection sort
that results when we put these pieces together.

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;

double temp = list[top];


list[top] = list[largeLoc];
list[largeLoc] = temp;
}
}

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.

Renée Brien Vincent Doris Scarlett

2. In the selectSort method shown in Example 3, what would happen


if the expression list[i] > list[largeLoc] were to be changed to
list[i] < list[largeLoc]?
10.5. BUBBLE SORT 395

3. In our version of selection sort, if the largest item is already at location


top in the list, then the method still swaps that value with itself, even
though that is not necessary.

(a) How could the method be changed to avoid this unnecessary


swapping?
(b) Why might it be better to leave the method as it is in the text?

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.

(a) Given the set of data


8 9 6 1 2 4
show the data as they would appear after each pass of a selection
sort using this algorithm.
(b) Write a Java method that implements this algorithm to sort an
array of int values.

5. Sometimes we are only interested in knowing the values that would


occupy one end of the list if the list were sorted. As an example, we
may want to know the scores of only the top ten competitors in a
contest. Modify the selection sort of Example 3 so that, instead of
sorting the entire array, it puts the k largest values in order in the
last k positions in the array. The value of k should be a parameter
of the method.

10.5 Bubble Sort

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

in alphabetical order. We start by comparing Phil to Ivan. Since they are


not in order, we exchange them to give

Ivan Phil Sara Jack Gina

Next we compare Phil to Sara. Since they are in order, we leave them that
way giving

Ivan Phil Sara Jack Gina

Now, comparing Sara to Jack, we see that they must be exchanged. Doing
this gives

Ivan Phil Jack Sara Gina

We then compare Sara to Gina. Again, we must perform an exchange to


obtain

Ivan Phil Jack Gina Sara

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

Phil Ivan Sara Jack Gina


10.5. BUBBLE SORT 397

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

Ivan : Phil Jack Gina | Sara


Ivan Phil : Jack Gina | Sara
Second Pass
Ivan Jack Phil : Gina | Sara
Ivan Jack Gina | Phil Sara

Ivan : Jack Gina | Phil Sara


Third Pass Ivan Jack : Gina | Phil Sara
Ivan Gina | Jack Phil Sara

Ivan : Gina | Jack Phil Sara


Fourth Pass
| Gina Ivan Jack Phil 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.

for (int top = list.length-1; top > 0; top--)


// compare adjacent values of the unsorted sublist
// from 0 to top, exchanging as necessary

Within each pass, the code for comparing and exchanging values in a
sublist is easily written.

for (int i = 0; i < top; i++)


if (list[i].compareTo(list[i+1]) > 0)
{
temp = list[i];
398 CHAPTER 10. SEARCHING AND SORTING

list[i] = list[i+1];
list[i+1] = temp;
}

Combining these fragments of code and inserting them into a method


gives us a basic bubble sort. We can improve this sort’s performance by
noting that, on each pass, we often do more than simply place the greatest
value at the upper end of a sublist; the comparisons and exchanges tend to
push all values toward their final positions in the list. It may happen that
this shuffling of values puts all the values in their final positions before all
passes have been completed. In such a situation, we should stop working
as soon as possible, avoiding any unnecessary passes. As it turns out, this
is not too difficult to do.
Before we incorporate this modification, we must first answer the fol-
lowing question: how do we know if there is no more work to be done?
The answer is: if no exchanges are performed in an entire pass, then the
items must be fully sorted. The reasoning here is that, if no exchanges are
needed, then each value must be correctly ordered between its immediate
neighbours. It follows that the entire set of values must, therefore, be com-
pletely ordered. Thus we need to perform another pass only if the previous
one carried out one or more exchanges.
To encode this idea in Java, the for statement that controls the num-
ber of passes should now incorporate a test to see if another pass is neces-
sary. We do this with a boolean variable called sorted. Initially, sorted is
set to false; we do not start a pass unless sorted is false. Once we have
started a pass, we (optimistically) set sorted to true because no exchanges
have yet been performed during that pass. If any exchanges are required
during a pass, we reset sorted to false. Processing continues until sorted
remains true after a full pass or we have completed the maximum number
of passes.

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.

public static void bubbleSort (String[] list)


{
boolean sorted = false;
10.5. BUBBLE SORT 399

for (int top = list.length-1; top > 0 && !sorted; top--)


{
sorted = true;
for (int i = 0; i < top; i++)
if (list[i].compareTo(list[i+1]) > 0)
{
sorted = false;
String temp = list[i];
list[i] = list[i+1];
list[i+1] = temp;
}
}
}

Although bubble sort uses an interesting technique and has a cute


name, we do not recommend it. It is almost always slower than either of
the sorts that we have already examined because it usually involves far
more data movement than they require.

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. What changes would have to be made to the bubbleSort method in


order to make it sort values in descending order?
3. A modification of the bubble sort is the cocktail shaker sort in which,
on odd-numbered passes, large values are carried to the top of the
list while, on even-numbered passes, small values are carried to the
bottom of the list. Make tables like those shown in Example 2 to
show the first two passes of a cocktail-shaker sort on the following
data.

2 9 4 6 1 7
400 CHAPTER 10. SEARCHING AND SORTING

4. Write a method shakerSort to implement the cocktail shaker sort al-


gorithm to arrange an array of double values in ascending order. Use
a boolean flag to stop processing once the items have been completely
sorted.

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

If we now sort each sublist, we obtain


12 17 29 34
11 13 21 27
18 25 38 40
to give us a 3-sorted list.

12 11 18 17 13 25 29 21 38 34 27 40

What does this achieve? If we examine the original list of values in


Example 1 together with the 3-sorted list and the list as it would be if it
were completely ordered, we can see that in the 3-sorted list, many of the
elements have been moved long distances and most of them are quite close
to their final destination.
Unsorted: 34 21 40 12 27 18 29 13 25 17 11 38
3-sorted: 12 11 18 17 13 25 29 21 38 34 27 40
Sorted: 11 12 13 17 18 21 25 27 29 34 38 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.

3. In the text, we suggested that a good sequence of values of k ended


with 40, 13, 4, 1. Give the next three smallest terms of this sequence.
10.6. SHELLSORT 403

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.)

10.7 Comparing Algorithms

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

Analysis of the performance of binary search requires a somewhat dif-


ferent approach. As with sequential search, we use the number of compar-
isons to obtain an approximation of the rate of growth of time, T , as the
size, n, of a list increases. Again, as with sequential search, in the worst case
we must continue to examine elements until the size of the list is reduced
to zero. Unlike the case with a sequential search, however, each time we
make a comparison with a binary search, we eliminate approximately half
of the remaining items. We let the total number of comparisons required
for a list of size n be cn . Since one comparison eliminates approximately
half the elements in the list, we have

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

After one more comparison, we have

cn =
˙ 3 + cn/8 = 3 + cn/23

After k comparisons, we have

cn =
˙ k + cn/2k

To actually determine the value of cn from this equation, we note that


exactly one comparison is required for a list of size one. Thus, if n/2k = 1,
cn/2k = 1 and hence

˙ k + 1 where n/2k = 1
cn =

Now, to obtain cn in terms of n, we solve the equation n/2k = 1 for k.


n
= 1
2k
2k = n
k = log 2 n

Because logarithms with base 2 appear so often in analysis of algorithms,


we often abbreviate the expression log 2 n as simply lg n. Substituting this
10.7. COMPARING ALGORITHMS 407

value for k in our equation for cn gives us

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)

To analyze the performance of selection sort we note that, once again,


the primary activity during execution of the sort is comparison of items.
The first pass requires n−1 comparisons to find the largest item, the second
pass requires n − 2 comparisons, the third pass requires n − 3 comparisons,
and so on until the last pass requires only one comparison. The total
number of comparisons is

(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)

We can simplify this expression even further. To do so, let us compare


values of the expression n2 − n with n2 .
408 CHAPTER 10. SEARCHING AND SORTING

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
=

Combining these equations gives

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

The sort would take about 5 s to sort 8000 items.

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

(a) 2n? (b) 3n?


(c) 5n? (d) 10n?

2. If an algorithm is known to be O(n3 ) 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
(a) 2n? (b) 4n?

3. Binary search is known to be O(lg n). Suppose that a binary search


requires c comparisons (in the worst case) to search a list of size n.
What is the maximum number of comparisons that the search would
require for a list of size
(a) 2n? (b) 4n?
(c) 8n? (d) 64n?

4. Suppose that, in a certain environment, selection sort requires 0.06 s


to sort a list of length 500.
(a) Estimate the time that it would require to sort a similar list of
length 50 000.
(b) Estimate the number of items that could be sorted in one hour.
5. Suppose that, in a certain environment, Shellsort requires 0.004 s to
sort a list containing 1000 items.
(a) Estimate the time that it would take to sort a similar list of
length 100 000.
(b) Estimate the number of items that could be sorted in one second.
6. Although Shellsort is more efficient than straight insertion sort, in-
sertion sort might be faster for short lists because the algorithm is
simpler. Suppose that, on a particular machine, both insertion sort
and Shellsort require 0.05 s to sort 200 items. Find the approximate
amount of time each one would take to sort 100 000 items.
7. Draw graphs showing the number of comparisons that would be re-
quired (in the worst case) by each of the following algorithms.
(a) sequential search
(b) binary search
(c) selection sort
10.8. COMPARING OBJECTS 411

10.8 Comparing Objects


Throughout this chapter, the examples we have shown for our sorts have
almost invariably used primitive types. Often, however, we want to sort
objects. As examples, we might want to order student record objects by
student numbers while we might want to order objects representing shapes
by size.
To sort objects, we can start by writing a sort that is applicable to
any objects that we might consider to be sortable. Let us designate such
objects as being of type Sortable. Of course, if we are going to sort these
objects, we must decide what it means for one object of type Sortable
to “precede” another object of the same type. Suppose, for now, that we
have done this by writing an instance method for Sortable objects with
the following signature
public boolean precedes (Sortable other)
The method precedes returns true if and only if its implicit object pre-
cedes other. Once we have a method that defines criteria for ordering
Sortable objects, we can then write a sort for such objects.

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

Our goal is to make this method applicable to a wide variety of objects,


unrelated except for the fact that each of them has some criterion for
ordering the objects. To do so we must do two things:

• 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.

• Show how each class that we want to be considered Sortable should


implement the 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

class Fraction implements Sortable


{
private int num;
private int den;

public boolean precedes (Sortable s)


{
Fraction f = (Fraction)s;
double thisValue = (double)num/den;
double otherValue = (double)f.num/f.den;
if (thisValue < otherValue)
return true;
else
return false;
}

// other methods of the class

}
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

in Java. In the java.lang package, there is an interface called Comparable


that contains the header of a single method called compareTo with the sig-
nature
public int compareTo (Object o)
Any class implementing the Comparable interface must supply a compareTo
method that accepts some kind of Object as a parameter and returns an
int value as specified in the following table

Relationship Between Objects Value Returned


implicit object precedes explicit object some negative integer
implicit object equals explicit object zero
implicit object follows explicit object some positive integer

We have already encountered the compareTo method in our study of


strings. The header of Java’s String class contains the phrase implements
Comparable and the class contains a compareTo method for String objects.

Example 3
An insertion sort that could be used for any objects that implement the
Comparable interface could take the following form.

public static void insertSort (Comparable[] list)


{
for (int top = 1; top < list.length; top++)
{
Comparable item = list[top];
int i = top;
while (i > 0 && item.compareTo(list[i-1]) < 0)
{
list[i] = list[i-1];
i--;
}
list[i] = item;
}
}
10.8. COMPARING OBJECTS 415

If we wanted to apply this sort to an array of Fraction objects, all


that we would have to do is add a compareTo method to our Fraction
class and insert the phrase implements Comparable in the header of the
Fraction class.

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.

class Fraction implements Comparable


{
private int num;
private int den;

public int compareTo (Object o)


{
Fraction other = (Fraction)o;
double thisValue = (double)num/den;
double otherValue = (double)other.num/other.den;
if (thisValue < otherValue)
return -1;
else if (thisValue == otherValue)
return 0;
else
return 1;
}

// other methods of the class

Since the parameter of the compareTo method in the Comparable interface


is Object, any compareTo method that we write must have a parameter of
that type. Within the method, we cast the parameter to a reference to a
Fraction object.
416 CHAPTER 10. SEARCHING AND SORTING

Using the Comparable interface, we have a technique that allows us


to use a single sorting method to sort any type of object that we create
but it cannot sort lists containing primitive types such as int or double.
To include these, Java also offers a number of wrapper classes that are
used, as their name suggests, to enclose a primitive type in an object. The
identifiers of the wrapper types are usually the same as the corresponding
primitive types but written with an initial upper case letter. For example,
the wrapper class for double values is Double while the wrapper class for
byte is Byte. The only exceptions to this are the wrapper classes for int
and char whose identifiers are Integer and Character. All the wrapper
classes (except Boolean) contain appropriate compareTo methods.

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.

public class SortDemo


{
public static void main (String[] args)
{
Fraction[] f = new Fraction[3];
f[0] = new Fraction(3,4);
f[1] = new Fraction(1,2);
f[2] = new Fraction(2,3);

Integer[] n = new Integer[3];


n[0] = new Integer(7);
n[1] = new Integer(5);
10.8. COMPARING OBJECTS 417

n[2] = new Integer(2);

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]);
}

public static void selectSort (Comparable[] list)


{
for (int top = list.length - 1; top > 0; top--)
{
int largeLoc = 0;
for (int i = 1; i <= top; i++)
if (list[largeLoc].compareTo(list[i]) < 0)
largeLoc = i;

Comparable temp = list[top];


list[top] = list[largeLoc];
list[largeLoc] = temp;
}
}
}

The output from this program would be


418 CHAPTER 10. SEARCHING AND SORTING

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

In Example 5, the compareTo method operates differently depending


on the type of object associated with it. The elements of the list array
can be of type String, Fraction, or Integer. At execution time, Java
determines what type of object is involved in the call and picks the ap-
propriate form of the compareTo method. This extremely useful feature is
an example of polymorphism, a word meaning “occurring in many forms”.
Java’s interfaces allow us to introduce polymorphism very easily. To invoke
polymorphism, an object can be associated with a method as an implicit
parameter, an explicit parameter, or both (as in the case of compareTo).

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

10.9 Review Exercises 10

1. For the list

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.

3. Rewrite the binary search method of Section 10.2 so that, if there is


more than one element in the list whose value is equal to item, the
method returns the index of the first such element in the list.

4. Suppose that the following list of values is to be sorted into ascending


order.

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

5. Suppose that the following list is to be arranged in ascending order.

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.

6. Suppose that insertion sort is to be used to sort a list of n integers.


Show that, in the worst case, the number of data movements required
is 12 n2 + 32 n − 2.
7. We might think of the process of shuffling (as used with decks of
playing cards) as the opposite of sorting. To investigate shuffling,
suppose that you are given an array called list of n int values.
Initially, list[i] has the value i.
(a) Suggest a reasonably efficient algorithm that could be used to
shuffle the numbers in list.
(b) Implement your algorithm as a Java method with heading
public static void shuffle (int[] list)
8. Write a program that reads up to 1000 positive integers. The values
are not necessarily all different and they are not in any particular
order. The program should continue reading input until it has read
1000 values or it has read the sentinel value, zero. The program
should then print all the distinct positive integers read (in ascending
order) together with a count of the number of occurrences of each
number. As an example, input of
5 7 2 5 4 9 4 7 5 1 0
should produce output like the following.
Value Frequency
1 1
2 1
4 2
5 3
7 2
9 1

Projects

9. Write a program to play the following game. Begin by displaying


the ten digits 0 - 9 in some random order. Then repeatedly ask
the person playing the game to choose a number n, after which the
computer will reverse the first n digits in the sequence. The object
of the game is for the player to put all the digits in ascending order
10.9. REVIEW EXERCISES 10 421

using as small a number of reversals as possible. Set up your program


so that after each game it asks if the game should be repeated with
the same starting sequence, played with a new starting sequence, or
terminated.
10. In the card game called bridge, each of the four players is dealt thir-
teen cards. To help players to evaluate the worth of their hands, it is
common to assign a numerical value to each hand. One widely used
system assigns values as follows:
• 4 points for each ace
• 3 points for each king
• 2 points for each queen
• 1 point for each jack
• 3 points for a void (no cards in a suit)
• 2 points for a singleton (one card in a suit)
• 1 point for a doubleton (two cards in a suit)
Write a program that will read a string representing a bridge hand.
The string should consist of 26 characters, two characters for each
card. For each card, the first character will represent the denomina-
tion (using A for ace, K for king, Q for queen, J for jack, T for ten,
and 2 to 9 for the other denominations) and the second character will
represent the suit (S for spades, H for hearts, D for diamonds, and C
for clubs). For example, the string
4DTCKC5H7CQSAH4H6SJHAC9H2H
would represent a hand containing the four of diamonds, ten of clubs,
king of clubs, and so on.
Your program should repeatedly ask the user if another hand
should be evaluated. If so, the program should prompt the user to
submit a hand, read the input, and determine whether or not the
input is valid. If the input is not valid, the program should reject it
and print a message. If it is valid, the program should evaluate the
hand and print a display of the hand together with its value. The
display should be arranged according to the following rules. Spades
should be listed on the first line, hearts on the second line, diamonds
on the third line, and clubs on the fourth line. Within each line, the
cards of a suit should be arranged in descending order of value (ace,
king, queen, jack, 10, 9, . . . , 2) with each value separated by one
422 CHAPTER 10. SEARCHING AND SORTING

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

Recursion is a powerful concept that is important in


computer science for two reasons. First, many seem-
ingly complex problems can be solved simply if we use
recursion. Second, certain types of information can be
represented easily using recursively defined data struc-
tures. Recursion is, essentially, a way of defining some-
thing in terms of itself. A recursive solution to a problem
is obtained through solving simpler versions of the orig-
inal problem while a recursive description of a structure
is obtained through the description of simpler versions
of the original structure. In this chapter, we begin by
examining situations from everyday life that illustrate
recursion and then apply recursive solutions to several
types of problems that can be solved using a computer.
424 CHAPTER 11. RECURSION

11.1 Everyday Recursion

We begin our study of recursion by examining situations from everyday life


that illustrate recursion.
If you wanted to draw a family tree showing an individual and all
of that person’s direct descendants, you would probably start by writing
down the name of the person. Under his or her name, you could then draw
lines to the name of each child of the person. If any of these children had
children, you could then draw lines to the names of their children. This
process could be continued, drawing lines to the names of the children’s
children, and so on until you reached a point (for each descendant) at
which there were no further descendants.
We can describe this process easily and concisely if we use 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

The example illustrates two features typical of recursive algorithms.


1. The simplest case of the problem can be solved directly. In the ex-
ample, a childless person is the simplest case; after writing the name
of such a person, that person’s family tree is complete.
2. All other cases of the problem are solved by doing a bit of work in
the direction of a solution and then solving one or more sub-problems
of the original problem. In the example, after writing the name of a
person with one or more children, we must draw the family tree of
each of these children.
11.1. EVERYDAY RECURSION 425

This type of analysis can be applied to many activities. We illustrate


a fairly silly one in the next example.

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.

In any recursive algorithm, it is crucial that we have a way of stopping


the process. This is often called a terminating condition of a recursive
algorithm. In Example 1, the terminating condition was the case in which
a person had no children. In Example 2, the terminating condition was the
arrival at the core of the onion.

Exercises 11.1
1. The following is a recursive definition of the process of climbing a set
of stairs.

To CLIMB THE STAIRS


if you are at the top
you are done
else
take one step and
CLIMB THE STAIRS
426 CHAPTER 11. RECURSION

(a) Identify the simple case in CLIMB THE STAIRS.

(b) Identify the complex case in CLIMB THE STAIRS.

2. Why is the terminating condition so important in a recursive algo-


rithm?

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 . . . ’ ”.

Is this an example of recursion? Justify your answer.

4. Which of the following can be said to be examples of recursion?

(a) closing a zipper

(b) nested Russian wooden dolls

(c) a reflection of a mirror in a mirror

(d) the song that begins “There’s a hole in my bucket . . . ”

5. Rewrite the definition of CLIMB THE STAIRS in Question 1 using a


while statement.

6. Give a recursive description of the process of reading a book.

7. In the text, we gave recursive algorithms for the tasks of drawing a


family tree and finding the centre of an onion. Find another task that
can be defined recursively and give the recursive algorithm for it.

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

Grace Liliane Kirk

Michael David Raymond Paul Eura

Anne Gerry

11.2 Recursion in Mathematics

Suppose that we are given the following sequence of integers:

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.

2. There is some way of stopping the recursion. This is done here by


explicitly defining the first term, t1 , as having the value 3.
428 CHAPTER 11. RECURSION

Example 1
Given the sequence defined recursively by the equations

t1 = 2
tn = 3 × tn−1 + 1, if n > 1

find the value of the fifth term, t5 .


From the definition, we know that

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

The value of the fifth term is 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.

gcd(54, 90) = gcd(90, 54) by Rule 3


= gcd(54, 36) by Rule 2
= gcd(36, 18) by Rule 2
= gcd(18, 18) by Rule 2
= 18 by Rule 1

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

2. Write a possible recursive definition of each sequence.


430 CHAPTER 11. RECURSION

1 3 5
(a) 10, 13, 16, 19, . . . (b) , , ,...
2 2 2

(c) 5, 15, 45, 135, . . . (d) −1, −3, −5, . . .

(e) 256, 64, 16, 4, . . . (f) 1.1, 1.2, 1.3, 1.4, . . .

3. Use Euclid’s algorithm to find the gcd of each pair of numbers.


(a) 18 and 24 (b) 84 and 144

(c) 35 and 16 (d) 515 and 206

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.

(a) State the lengths of the first four jumps.


(b) State an expression for the length in metres of the nth jump, jn ,
in terms of the (n − 1)th jump, jn−1 .
(c) Use the recursive relationship established in part b) to find the
distance jumped in the sixth jump.
(d) Give a non-recursive expression for jn , the length in metres of
the nth jump.

5. (a) If we were to use Euclid’s algorithm, as given in the text, to find


the gcd of 44 and 6, how many subtractions would be required
to reduce the problem to that of finding the gcd of 6 and 2?
(b) What operation would achieve the same effect as this sequence
of subtractions?
(c) Suggest a modification of the algorithm that avoids this sequence
of subtractions.
(d) Use the modified algorithm to find the gcd of each pair of num-
bers in Question 3.

6. Prove that Euclid’s algorithm, as stated in the text, correctly finds


the gcd of two positive integers, m and n.
11.3. RECURSIVE QUERIES 431

11.3 Recursive Queries

Suppose that we are given the following recursive definition of a sequence

t1 = 2
tn = 3tn−1 − 1, n > 1

We can express such a sequence using function notation if we write

t(1) = 2
t(n) = 3t(n − 1) − 1, n > 1

To implement such a function with a Java method is simple. (The tricky


part is understanding how the method operates.)

Example 1
The following method returns the value of the function defined by

t(1) = 2
t(n) = 3t(n − 1) − 1, n > 1

If the method is given an invalid value of the parameter, n, it throws an


exception.
public static int term (int n)
{
if (n < 1)
throw new RuntimeException("Invalid parameter");
else if (n == 1)
return 2;
else
return 3 * term(n-1) - 1;
}
432 CHAPTER 11. RECURSION

At first glance, the method may not appear to do anything; it seems


to be nothing more than a definition of the sequence. To see how it works,
suppose that term is called with n = 4. Since n > 1, the method executes
the statement

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.

We can illustrate this process using a diagram.

original call
term (n = 4)
executes  
return 3 *  term(3)- 1;
calls 

term (n = 3)

This copy of term (with n = 3) now executes the statement

term = 3 * term(2) - 1;

Before it can complete execution of this statement, it must call another


copy of term, this time with n = 2. The process continues until a copy is
called with n = 1, as shown in the next diagram.
11.3. RECURSIVE QUERIES 433

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

to the point from which it was called


434 CHAPTER 11. RECURSION

A silly but possibly useful way of looking at this process is to imagine


yourself trying to find the value of t(4). Rather than doing all the calcu-
lations yourself, you phone your friend Alex. Alex knows the formula for
t(n), so he can see that
t(4) = 3 × t(3) − 1.
Unfortunately, he doesn’t know t(3) so he puts you on hold while he calls
his friend Kim. Kim also knows the formula so she can quickly determine
that
t(3) = 3 × t(2) − 1.
To help her complete her calculations, Kim must have the value of t(2). To
get this, she puts Alex on hold while she phones her friend Chris. Chris
also knows the formula so he can see that the answer to Kim’s problem is
t(2) = 3 × t(1) − 1
Like the other callers before him, Chris doesn’t want to do all the work so
he puts Kim on hold while he phones his friend Wei Ting to find t(1). Wei
Ting can see from the formula that
t(1) = 2
Wei Ting tells Chris that the answer is 2 and then hangs up (wondering
why Chris would phone to ask her such a question).
Chris can now complete his calculations:
t(2) = 3 × 2 − 1 = 5
Chris passes this information back to Kim and then he too hangs up.
Kim can now determine that:
t(3) = 3 × 5 − 1 = 14
Kim tells Alex her result and then she also hangs up.
Alex can now complete his calculation:
t(4) = 3 × 14 − 1 = 41
He passes this back to you and hangs up, leaving you to do what you want
with the knowledge that
t(4) = 41

If all of this calling and returning seems to you to be a waste of time,


you are right! Recursion is a very useful technique but it does not provide
the most efficient way of evaluating a function like the one that we have
here. We have shown this example only because it illustrates some aspects
of recursion — not because we recommend that you use it in such circum-
stances.
11.3. RECURSIVE QUERIES 435

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.

public static int term (int n)


{
if (n < 1)
throw new RuntimeException("Invalid parameter");
else
{
int value = 2;
for (int i = 2; i <= n; i++)
value = 3 * value - 1;
return value;
}
}

Recursive methods in Java are not limited to evaluating terms of se-


quences. They can be used to evaluate any function for which we have a
recursive definition.

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

public static int gcd (int m, int n)


{
if (n == 0)
return m;
else
return gcd(n, m % n);
}

If we make a call to gcd(24,18), we produce the following sequence


of actions:
original call
gcd (m = 24, n = 18)
executes  
return gcd(18,24 % 18);

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.

public static double term (int n)


{
if (n < 0)
{
System.out.println("Invalid argument to method"
+ " term\nzero returned");
return 0;
}
else if (n == 0)
return 1;
else
return 2 + 0.5 * term(n-1);
}

3. What happens if the function term in Example 1 is called with a


parameter whose value is zero?

4. What is wrong with these methods?


(a) public static double bad (double a, double b)
{
a = a/2;
b = b*2;
return bad(a,b);
}

(b) public static int badToo (int n)


{
if (n < 1)
return 0;
else if (n == 1)
return 5;
else return 2*badToo(n+1) + 3;
}
438 CHAPTER 11. RECURSION

5. The factorial of a non-negative integer n (written as n!), is a function


that is useful in many branches of mathematics. It can be defined
recursively as follows:

1 if n = 0
n! =
n × (n − 1)! if n > 0

(a) Write a recursive method factorial that returns the value of


n! as a long value. (A long can represent n! for values of n up
to 20.) If an invalid parameter value is given to the method, it
should print an error message and return zero.
(b) Write a non-recursive version of factorial.
(c) Which version do you think would be more efficient? Justify
your answer.

6. A function f is defined for integers x and y as follows:



 −f(y, x) if x < y
f(x, y) = 0 if x = y

1 + f(x − 1, y) if x > y

(a) Use this definition to evaluate


i) f(5, 3) ii) f(2, 2) iii) f(1, 4) iv) f(−5, −2)
(b) State, in a few words, the effect of the function f if it has integer
arguments x and y.

7. A function that is important in the study of the theory of computation


(but quite useless otherwise) is known as Ackermann’s function. It
can be defined for non-negative integers m and n as follows:

 n+1 if m = 0
a(m, n) = a(m − 1, 1) if m > 0 and n = 0

a(m − 1, a(m, n − 1)) otherwise

(a) Evaluate a(1, 1) and a(2, 1) by hand.


(b) Write a Java implementation of Ackermann’s function.
(c) Try to use your implementation to evaluate a(5, 5). Account for
the result.
11.4. RECURSIVE COMMANDS 439

11.4 Recursive Commands

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:

1. Only one disk may be moved at a time.

2. A disk may not be placed on top of a smaller disk.

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:

• Move the one-disk tower from Needle 1 to Needle 2. (1 −→ 2)

• Move the bottom disk from Needle 1 to Needle 3. (1 −→ 3)

• Move the one-disk tower from Needle 2 to Needle 3. (2 −→ 3)


440 CHAPTER 11. RECURSION

Similarly, a tower with three disks can be thought of as a two-disk


tower placed on top of the largest disk. The instructions for moving this
tower from Needle 1 to Needle 3 could then take the form:
• Move the two-disk tower from Needle 1 to Needle 2. This is a problem
that we can solve using the procedure described above for a two-disk
tower.
The solution is: (1 −→ 3 1 −→ 2 3 −→ 2)
• Move the bottom disk from Needle 1 to Needle 3 (1 −→ 3)
• Move the two-disk tower from Needle 2 to Needle 3. This is also a
problem that we can solve using the procedure described above for a
two-disk tower.
The solution is: (2 −→ 1 2 −→ 3 1 −→ 3)
The pattern should now be clear. If there are n disks on Needle 1,
then we can think of this as a tower of height n − 1 placed on top of the
largest disk. To move the tower to Needle 3, we proceed as follows:
• Move the (n − 1)-disk tower from Needle 1 to Needle 2.


1 
2 
3 
q
q
   
 n   n − 1 
Needle 1 Needle 2 Needle 3

• Move the bottom disk from Needle 1 to Needle 3.


1 
2 
3 
q
q
   
 n−1   n 
Needle 1 Needle 2 Needle 3

• Move the (n − 1)-disk tower from Needle 2 to Needle 3.


11.4. RECURSIVE COMMANDS 441


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:

To MOVE A TOWER of n disks


if n = 1
simply move the disk
else
MOVE A TOWER of n-1 disks from the first needle
to the intermediate needle
move the bottom disk from the first needle
to the last needle
MOVE A TOWER of n-1 disks from the intermediate needle
to the last needle

We use this algorithm in the recursive method moveTower that follows.


The method’s three parameters represent the height of the tower to be
moved, the number of the needle on which the disks start, and the number
of the needle on which the disks finish. The method prints the appropriate
sequence of moves for such a tower. The method does not check for invalid
parameter values; it assumes that the height is positive and that the start
and finish needle numbers are two of the values 1, 2, or 3.

public static void moveTower (int height, int start,


int finish)
{
442 CHAPTER 11. RECURSION

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);

// move all but one disk from start to other


moveTower(height-1,start,other);

// move the bottom disk


System.out.println(start + " ---> " + finish);

// move the remaining disks from other to finish


moveTower(height-1,other,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

Tracing the action of a recursive method like moveTower in which there


is more than one recursive call is, in general, not an easy task. A carefully
drawn diagram, however, can often be helpful. The following diagram
traces the action of moveTower(3,1,3); The numbers shown in circles
indicate the order in which the calls and returns would be performed.
11.4. RECURSIVE COMMANDS 443

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

The sequence of moves can be read from left to right:


1 −→ 3 1 −→ 2 3 −→ 2 1 −→ 3 2 −→ 1 2 −→ 3 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

(c) The Towers of Hanoi problem is said to be based on a legend in


which monks in a monastery near Hanoi came to be in posses-
sion of sixty-four golden disks mounted on one of three diamond
needles. The monks’ task was to attempt to move all the disks
to another needle using the rules stated in the text. According
to the story, after all the disks have been moved, the universe
will come to an end. If we suppose that the monks began their
task three thousand years ago and that they have been moving
disks constantly at a rate of one per second ever since, find the
approximate number of years from now to the time when the
universe should end.
2. Trace the action of moveTower(4,1,3);
3. (a) What will moveTower do if it is passed a zero or negative value
for height?
(b) What will it do if it is given an invalid value of start or finish?
(c) Modify moveTower so that it will give an error message and
attempt no moves if it is given invalid values for any of its pa-
rameters.
4. Write a program that uses moveTower to print the moves required
to move n disks from Needle 1 to Needle 3 in the Towers of Hanoi
problem. Your program should ask the user to give a positive value
for the height of the tower (and should insist on being given a valid
height before it starts to move the tower).
5. Study the method convert and then answer the questions that follow
it.

public static void convert (int value, int base)


{
if (value >= base)
convert(value/base,base);
System.out.print(value%base);
}

(a) Trace the action of the method if it is called by the statement


convert(10,2);
(b) Trace the action of the method if it is called by the statement
convert(22,3);
11.4. RECURSIVE COMMANDS 445

(c) Describe in a few words the purpose of the method.


(d) What would the method do if called with value < 0 and base
> 0?
(e) Rewrite the method so that it works correctly even if value is
negative.

6. (a) Give a recursive description of the process of printing a row


containing n asterisks.
(b) Complete the definition of a recursive method printRow whose
header is shown below. The method should print a line contain-
ing a row of n asterisks. If n is less than one, the method should
print nothing.
public static void printRow (int n)

7. Suppose that the following pattern is called a 5-triangle.

*
**
***
****
*****

(a) Give a recursive description of the process of printing an n-


triangle.
(b) Write a recursive method printTriangle with a single int pa-
rameter n. The method should print a triangle of asterisks like
the one shown here but containing n rows. If n is less than one,
the method should print nothing.

8. What will the method mystery do if called with the argument 12345?

public static void mystery (int n)


{
if (n > 0)
{
System.out.println(n);
mystery(n/10);
}
}
446 CHAPTER 11. RECURSION

9. What output will the following methods produce if originally called


by the statement q(4,’*’);?

public static void p (int n, char symbol)


{
if (n > 0)
{
System.out.print(symbol);
p(n-1,symbol);
}
else
System.out.println();
}

public static void q (int n, char symbol)


{
if (n > 0)
{
p(n,symbol);
q(n-1,symbol);
}
}

11.5 Recursion with Strings

Although the applications of recursion that we have seen up to now are


primarily concerned with numerical values, recursion can also be used to
solve non-numerical problems. In this section, we will explore some uses of
recursion in manipulating strings.
To develop recursive algorithms for strings, we must think of a non-
empty string as a recursive structure. We can do this by considering a
non-empty string as a character concatenated with a shorter string. An
empty string, one with no characters, serves as a simple case.
11.5. RECURSION WITH STRINGS 447

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 "";
}

A permutation of a set of items is an arrangement of those items.


We can use recursion, along with a loop, to find all permutations of the
characters of a string. All permutations can be formed by placing each
character, in turn, in the first position of the string and following it with
all possible permutations of the other characters. For example, if the string
has the value "abcd", we can find its permutations systematically by taking
"a" followed by all permutations of "bcd"
"b" followed by all permutations of "acd"
"c" followed by all permutations of "abd"
"d" followed by all permutations of "abc"
The process can be continued recursively. At any point in the string, we
can generate all permutations of characters from that point to the end by
placing each character at the beginning of that substring and following it
with all possible permutations of the remaining characters. To make this
slightly more formal, we can state our algorithm as follows:
To PRINT STRINGS WITH ALL PERMUTATIONS of a string
if we are at the end of the string
print the current permutation
448 CHAPTER 11. RECURSION

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);
}

The inconvenience of having to specify a starting point for the permu-


tation can be avoided. To do so, we can overload permute by writing two
versions. The first version could take the following form:
public static void permute (String s)
{
permute(s,0);
}
The second version would take the form seen in Example 2, with a second
parameter denoting the current position in the string. The second version,
called a helper method, would then call itself recursively to print all permu-
tations of the characters. A user does not need to worry about specifying
the starting point for the permutation required by the helper method. In
fact, by making the helper method private, we can actually prevent a user
from using the recursive version directly.
11.5. RECURSION WITH STRINGS 449

Example 3
The pair of methods that follow, both called permute, work together to
find and print all the permutations of a string.

public static void permute (String s)


{
permute(s,0);
}

private 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);
}

If we were to place the methods in a class called Utilities, then, to print


all permutations of the string "abcd", we could write
Utilities.permute("abcd");

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

Write a recursive method reverse that implements this algorithm.


The method should have a header identical to that in Example 1.
450 CHAPTER 11. RECURSION

2. Yet another way to create the reversal of a string is to place the


reversal of the second half in front of the reversal of the first half.
As an example, to obtain the reversal of the string "turnabout", we
would concatenate the reversal of "bout" to ’a’ to the reversal of
"turn".
(a) State this algorithm by completing the following, using the style
shown in the previous question.
To REVERSE a string ...
(b) Write a recursive method reverse that implements this algo-
rithm. The method should have a header identical to that in
Example 1.
3. Assuming that the methods shown in Example 3 are in the class
Utilities, determine the output that would be produced by the
statement
Utilities.permute("cat");

11.6 Backtracking Algorithms

Many experiments in psychology involve rats or other animals attempting


to travel through a maze in order to receive some reward. On the assump-
tion that any task that can be performed by a rat should also be within
our grasp, we now examine the problem of programming the solution to a
maze problem.
To represent our mazes, we will use objects containing rectangular
arrays of characters in which the symbol ’.’ represents an open path, ’W’
represents a wall, and ’X’ represents an exit.1 A typical maze array is
shown here.
W W W W W W W W W W W
W . . . W . W . . . W
W . W . W . W . W W W
W . W . . . . . . . W
W . W W W W . W W . W
W . W . . . . . W . W
W W W W W W W X W W W

1 In Chapter 13 we examine techniques for producing much better graphical displays


but this will do for now.
11.6. BACKTRACKING ALGORITHMS 451

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

To complete the Maze class, we need a constructor that creates a new


maze and a method that prints the contents of a map. You are asked to
write these methods in the exercises. The key method required by the
Maze class is one that attempts to find the appropriate sequence of moves
to an exit from a given location. The next example contains the code for
a recursive method that performs this task.

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.

public boolean findExitFrom (int row, int col)


{
boolean successful = false;
if (map[row][col] == Maze.EXIT)
{
map[row][col] = Maze.GOOD_PATH;
successful = true;
}
else
{
map[row][col] = Maze.TRIED;
// try moving south
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 south - try moving east
11.6. BACKTRACKING ALGORITHMS 453

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.

3. Repeat the previous question if the method findExitFrom tried to


move to an adjacent position in the order: north, west, south, east.

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.

6. Write a complete program that creates a maze, prints it, prompts


the user for a starting position in the maze, attempts to find an exit
from that point, and prints the resulting maze, along with a message
indicating whether or not the search for an exit was successful.

7. Suppose that we are given a rectangular array containing blanks on


the perimeter and either blanks or asterisks on the interior, as shown
in the following diagram:

** *
**** *** **
** ** ****
*** **** ****
* ** ****
****** **
** *
** ******
* **

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.

(a) What is the simplest case of this problem?


(b) Give a recursive algorithm that could be used to erase a blob.
11.7. QUICKSORT 455

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

A pass of quicksort chooses one value K in an unsorted list to be


inserted in its correct location. This element is called a pivot. The pass
then rearranges the other elements, if necessary, so that all values less than
the pivot are on its left and all values greater than the pivot are on its
right. The values other than K may still be out of order but K will surely
be in the position that it will occupy once the list is fully sorted. We call
such a rearrangement a partition of the list.
To achieve a partition, we will use two markers to locate values that
are on the wrong end of the list and send them over to the correct end. Let
us see how to partition a list by looking at an example.

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

If all the partitions of Example 1 and Example 2 are performed, the


list would then have the following appearance:
27 14 12 37 52 55 46 61 63 71 74 91 87
To completely sort a list, quicksort continues to partition sublists as
long as the sublists contain more than one item. If a sublist contains fewer
than two items, no further work needs to be done on it. A Java method
to perform quicksort on an array of items of type Object is shown in the
next example.

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);
}

private static void quickSort (Object[] list,


int low, int high)
{
final int MOVING_LEFT = 0;
final int MOVING_RIGHT = 1;

if (low < high)


{
int left = low;
int right = high;
int currentDirection = MOVING_LEFT;
Object pivot = list[low];
while (left < right)
460 CHAPTER 11. RECURSION

{
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);
}
}

Does quicksort deserve its name? In general, it does. It is, on average,


faster than any of the other sorts that we have examined in this text.
It does, however, have its problems. For short lists, it is more trouble
than it is worth; the bookkeeping involved in the recursive calls wastes
too much time in this situation and it might be better to use Shellsort
or (for very short lists) selection sort or insertion sort. Another problem
arises if the partitions do not split the sublists near their midpoints. If the
number of such bad partitions becomes very large, the efficiency of quicksort
can be eroded very badly. A number of techniques have been found for
avoiding bad partitions but they are beyond the scope of this book.
11.8. ALGORITHM ANALYSIS REVISITED 461

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

2. Suppose that quicksort is used to sort 31 elements and suppose further


that every partition produces sublists containing an equal number of
elements.

(a) How many calls are made to the method quickSort?


(b) What is the total length of all lists passed to quickSort?

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.

4. (a) Compare the results of the preceding two questions.


(b) Can you suggest a modification in the way that the pivot is
selected that would enable quicksort to operate efficiently even
if the items are already ordered (or nearly so)?

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.

11.8 Algorithm Analysis Revisited

In the previous chapter, we introduced the idea of analysis of the perfor-


mance of algorithms and developed the use of big oh notation to describe
this performance. Having introduced some new algorithms in this chapter,
we can now extend these ideas.
In analyzing the behaviour of searching and sorting algorithms, we
used the number of comparisons to obtain an estimate of the rate of growth
462 CHAPTER 11. RECURSION

of time, T , as the size, n, of the problem increased. To analyze the be-


haviour of our algorithm to solve the Towers of Hanoi problem, we use the
number of moves to obtain an approximation of the rate of growth of T as
the number of disks, n, increases. Suppose that we let the number of moves
required to move a tower of n disks be mn . The recursive solution to the
problem says that, to move a tower of height n, we first move a tower of
height n − 1 out of the way, then move the bottom disk, and finally move
the tower of height n − 1 to the final position. Thus

mn = mn−1 + 1 + mn−1
= 2mn−1 + 1

Similarly, to move a tower of height n − 1, we have

mn−1 = 2mn−2 + 1

Substituting the second equation into the first gives

mn = 2(2mn−2 + 1) + 1
= 22 mn−2 + 2 + 1

Substituting mn−2 = 2mn−3 + 1 into this equation gives

mn = 22 (2mn−3 + 1) + 2 + 1
= 23 mn−3 + 22 + 2 + 1

After k steps, we have

mn = 2k mn−k + 2k−1 + · · · + 22 + 2 + 1

When k = n − 1, this gives

mn = 2n−1 mn−(n−1) + 2n−1−1 + · · · + 22 + 2 + 1


= 2n−1 m1 + 2n−2 + · · · + 22 + 2 + 1

Since m1 = 1, this simplifies to

mn = 2n−1 + 2n−2 + · · · + 22 + 2 + 1
11.8. ALGORITHM ANALYSIS REVISITED 463

Summing the geometric series on the right side, we have, finally

mn = 2n − 1

For large values of n, this gives


.
mn = 2n

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 )

To analyze the performance of quicksort, we proceed as we did for


the sorts in Chapter 10, by examining the number of comparisons. In our
analysis, we will make the assumption that each partition splits its sublist
into equal (or nearly equal) parts. As we noted when we were developing
the quicksort algorithm, our version of quicksort can be modified fairly
easily to guarantee that this will be the case.
On our first pass, we examine approximately n2 values on the left end
of the list and n2 values on the right end of the list, a total of n2 + n2 = n
comparisons.
pivot
?
n n
2 2
We can then repeat the process on both halves of the list. In each
half, we examine approximately n4 values on each side of the pivot, a total
of n4 + n4 + n4 + n4 = n comparisons. (In counting comparisons, we ignore
the fact that the old pivot is no longer being examined.)
pivot pivot
? ?
n n n n
4 4 4 4
Repeating the process in each of the four sub-lists requires the exami-
nation of approximately n8 + n8 + n8 + n8 + n8 + n8 + n8 + n8 = n comparisons.
pivot pivot pivot pivot
? ? ? ?
n n n n n n n n
8 8 8 8 8 8 8 8
464 CHAPTER 11. RECURSION

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)

Since we are dealing with proportionalities, we can divide by k to obtain


the usual representation if the time required by an algorithm is bounded
by some constant:
T is O(1)

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

A number of things can be deduced from an examination of the table. For


example, it is obvious that, for large amounts of data, an algorithm like
quicksort that is O(n lg n), is far better than an algorithm like selection
sort that is O(n2 ). Also, we can see that O(2n ) algorithms are hopelessly
slow for all but the smallest values of n.
11.8. ALGORITHM ANALYSIS REVISITED 465

In comparing algorithms, we must keep in mind that big oh notation


ignores constants. For small values of n, an algorithm for which the running
time is 5n2 is preferable to one whose running time is 5000 n lg n. Only
when n is large will this O(n lg n) algorithm outperform this O(n2 ) one.

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

2. Suppose that the running time of Algorithm A is 400n2 while the


running time of Algorithm B is 0.2n3. For what values of n will
Algorithm A run more quickly than Algorithm B?

3. In this section, we showed that the number of moves, mn , to move a


tower of height n in the Towers of Hanoi problem using our recursive
algorithm, is 2n −1. Use mathematical induction to prove this result.

4. Run an experiment to compare the running times of quicksort and


selection sort. For the experiment, use arrays of random integers in
the range 0 . . . 99999. Use the method currentTimeMillis in the
class java.lang.System to time the sorts. Experiment with various
sizes of arrays, starting at 1000 and then doubling the length on
each run until you get some useful results without using too many
computer resources (either space or time).
466 CHAPTER 11. RECURSION

11.9 Avoiding Errors and Debugging

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

1. If you happen to write something in which the recursion never ter-


minates, the recursive method will continue to call copies of itself.
Each of these calls requires space, to keep track of the values of pa-
rameters and local variables. Eventually, there will be no room left
in the computer for any more calls. In such a case, execution of your
program will likely terminate with a message about stack overflow.
If you get such a message, this is the likely cause.
2. Start debugging recursive methods by testing the simple case first.
Only after you are sure that this is working should you move on to
testing with more complex cases.
11.9. AVOIDING ERRORS AND DEBUGGING 467

3. Insert tracing statements to print the value of key variables each


time the recursive method is called. If the program is experiencing
problems with runaway recursion, these tracing statements will likely
fly past your face on the screen when you run the program. To make
the tracing statements readable, insert requests for input just after
the values are printed.

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

3. Consider the following definition of a sequence.

t1 = 1
t2 = 2
t3 = 3
tn = tn−1 + tn−3 , if n > 3
468 CHAPTER 11. RECURSION

(a) Write the first six terms of the sequence.


(b) Write a non-recursive method (with a single int parameter n)
that returns the value of the nth term of the sequence. If n < 1,
the method should return zero.

11.10 Review Exercises 11

1. Give a recursive description of the process of eating a bag of chips.

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

(b) Write a recursive Java method that implements the function.

4. The Fibonacci sequence consists of the numbers

1, 1, 2, 3, 5, 8, . . .

and can be defined recursively as follows:



 1 if n = 1
f(n) = 1 if n = 2

f(n − 1) + f(n − 2) if n > 2

(a) Write a recursive method fibonacci with an integer parameter


n. The method should return the nth term of the Fibonacci
sequence. (If n < 1, the method should produce an error message
and return the value zero.)
11.10. REVIEW EXERCISES 11 469

(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?

5. Write a recursive method digitSum that has a single int parameter.


The method should return the sum of the digits of the parameter.

6. Consider the pattern shown in the following diagram to be of size 4


because the longest row contains four asterisks.

****
***
**
*
**
***
****

(a) Give a recursive description of the process of printing a similar


pattern, of size n.
(b) Write a recursive method printPattern that will print a pat-
tern like the one shown here. The method should have a single
parameter n that specifies the length of the longest row in the
pattern. If n is less than one, the method should print nothing.

7. The Tchebyshev polynomials can be defined as follows:



 1 if n = 0
Tn (x) = x if n = 1

x · Tn−1 (x) − Tn−2 (x) if n > 1

(a) Find T2 (x) and T3 (x) in simplified form.


(b) Find the value of T4 (2) by hand.
(c) Complete the definition of a method tchebyshev whose header
is shown here. The method should return the value of Tn (x). If
n < 0, the method should return the value zero.
public static double tchebyshev (int n, double x)
470 CHAPTER 11. RECURSION

8. To compute the value of xn , where n is a positive integer, a recursive


solution can be obtained using the following relations:
 
 x n2 2 if n is even
n
x =  (n−1)
2
 x 2 × x if n is odd

For example, 46 = (43 )2


= ((4)2 × 4)2
Once the expression has been factored, we can then evaluate it with
only a few multiplications. Using the preceding example, we have

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 .)

9. In Section 10.2, we developed a method that used binary search to


determine whether or not an item was in a sorted list. The method
had the header
public static int binSearch(double[] list,double item)
The method returns the index in list of item (if item is in list)
and −1 otherwise. Rewrite the method so that it uses recursion to
perform its task. To carry out the recursion, use a helper method
with the header
private static binSearch (double[] list, double item,
int low, int high)
11.10. REVIEW EXERCISES 11 471

10. A polynomial of the form

p(x) = a0 + a1 x + a2 x2 + · · · + ai xi + · · · + an xn

can be evaluated efficiently using a technique known as Horner’s rule.


Horner’s rule avoids computing the numerous xi values explicitly,
since this is redundant and slow, by rewriting and evaluating p(x) in
the following form:

p(x) = a0 + x × (a1 + x × (a2 + · · · + x × (ai + · · · + x × (an ) · · ·) · · ·))

Complete the definition of the method horner with header


public static double horner (double[] a, double x)
so that it uses Horner’s method to evaluate a polynomial. The first
parameter is an array of coefficients of the polynomial, the second
is the value of x at which the polynomial is to be evaluated. The
method should use a recursive helper method with the header
private static double horner(double[] a,double x,int i)
The third parameter in the helper method is an index that you should
use to keep track of your recursion.
11. Write a method printSub with an int parameter sum and an int
array parameter list. The method should look for a sublist of list
whose elements add up to sum. If it finds such a sublist, it should
print it. For example, if list = {3,7,5,3,8} and sum = 12, then
the method should print 7 and 5. You may assume that there will be
no more than one sublist satisfying the condition.
12. The number five can be written as the sum of three positive integers
in two different ways: 5 = 1 + 1 + 3 and 5 = 1 + 2 + 2. (Different
orderings of the same values are not to be counted.) Write a method
with header
public static void partition (int n, int r)
that uses a recursive helper method to print all the ways that the
positive integer n can be written using r positive integers.

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

arbitrarily close to the actual values. There are a number of methods


that can be used to do this. To find a root of the equation f(x) = 0,
we can begin by considering the related function y = f(x) and try to
find an x-intercept of this function. An x-intercept of the function is
a root of the corresponding equation.
One such method is known as the bisection method. With this
method, we start the process with an interval [xLeft,xRight] in
which the function f is known to be continuous and in which the
sign of f(xLeft) is different from the sign of f(xRight). Since the
function is continuous and it changes sign in the interval, it must
have an x-intercept somewhere in the interval.
We can find a smaller interval containing the root by first finding
the midpoint, xMid, of the interval and then calculating the value of
f(xMid). If the value of f(xMid) is exactly zero, then we have found
the exact root of our equation. If it has a value whose sign is opposite
to that of f(xLeft), then the root must lie between xLeft and xMid.
If it has a value whose sign is opposite to that of f(xRight), then the
root must lie between xMid and xRight. By repeating this process,
we can find smaller and smaller intervals containing the root of the
equation. The process can be terminated either by finding an exact
root or by narrowing the interval in which the root must lie to be less
than some specified value epsilon.
Your problem is to write a program that uses the bisection
method recursively to solve a cubic equation of the form

ax3 + bx2 + cx + d = 0

Your program should begin by reading (as double values, each on a


separate line) the left and right ends of an initial interval and a value
of epsilon. The program should then use the bisection method to
find a root of the equation in the interval specified by epsilon. Your
program should continue to read values and find roots until the user
supplies input consisting only of zeros. Assume that all input will be
valid. Your program should consist of three methods in a single class:
• a method that calculates the value of a cubic function. The
method should have a header of the form
public static double f (double x)
It should calculate and return the value of
f(x) = 2x3 − 4x2 − 10x + 15
11.10. REVIEW EXERCISES 11 473

• a recursive method that uses the bisection method to find an


x-intercept of the cubic function (and hence a root of the corre-
sponding equation)
• a main method to do the reading of input, writing of output,
and calling of the method to do the bisection
14. In the game of chess, the queen is the most powerful piece. It can
attack another piece that lies on the same row, the same column, or
the same diagonal as the queen. Despite this power, it is still possible
to place many queens on a board at the same time so that none of
them can attack any of the others. As an example, the diagram shows
four queens distributed on a 4 × 4 board so that none threatens any
other.

Q
Q
Q
Q

Your problem is to write a program that uses recursion with back-


tracking to attempt to place n queens on an n × n board. To solve
the problem, it is useful to think of the board as consisting of n rows
each containing n columns rather than simply a board of n2 squares.
Since a queen can move any number of squares vertically in a single
move, there can be no more than one queen in each column. Since
you are required to place n queens on the board, there must be ex-
actly one queen in every column. A one-dimensional array of size n
can be used to record an entire solution. Using the previous example,
the arrangement of the four queens can be represented by the array

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

or not it is in an occupied row. Checking to see that it is in a safe


diagonal is trickier but it can be done efficiently and your program
should do so. Your program should first prompt the user for a board
size and then continue to attempt to place queens in columns until all
columns have been assigned a queen or no safe position can be found
for a queen in one of the columns. If a solution is found, the program
should print the contents of the array indicating the positions of the
queens.
15. Yet another sorting technique is the merge sort.3 As with quicksort,
the algorithm for a merge sort can be stated most easily in recursive
form, as follows:

To SORT a list of n items


if (n > 1)
{
SORT the left half of the list
SORT the right half of the list
MERGE the two sorted halves
}

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

Merging is done non-recursively, using, essentially, only one pass


through the list.
Write a recursive merge sort and then test it using arrays of ran-
domly generated integers in the range 0 . . . 99999 and sorting them
into ascending order. Use an insertion sort to perform the same task
and compare the running times of the two sorts. Use the method
currentTimeMillis in the class java.lang.System to time the sorts.
Experiment with various sizes of arrays, starting at 5 000 and then
doubling the length on each run until you get some useful results.
3 Merge sort is another example of an O(n lg n) algorithm.
Real Jobs — Real People
Name: Wendy Roth
Title: President
Company: WRPD & Associates Inc.
Education: B.Math., University of Waterloo (1986)

Q: Please describe a bit about your career history.


A: I graduated from the University of Waterloo with a Bachelor of Mathe-
matics, through the Co-Operative (Co-Op) program. The Co-Op program
enabled me to work at a wide variety of IT functions and I very much
enjoyed working in various environments and cultures. After graduation
from Waterloo, I went directly into IT consulting and have been doing
consulting ever since. I have been an external consultant for most of my
career, but I have also held several internal consulting roles while working
for organizations.
Roughly ten years ago, I decided to forge out on my own and begin
consulting as a self-employed independent contractor. Being self-employed
is never easy; it requires a great deal of dedication and discipline, but
it is well worth the effort. I very much enjoy having my independence
and selecting those assignments where I can provide the most value to
organizations. What I like the most about my work is that I can come into
an organization from the outside and make a difference to the way they
operate, think, and manage.
My main areas of expertise are project management, change man-
agement, and business process around the use of technology. I also have
expertise in the sales and marketing of IT. I have worked in a wide vari-
ety of industries, including sales and marketing, call centres, government,
human resources, oil and gas, finance, and law.
Q: Which projects have you found really interesting and why?
A: I find projects that truly drive significant business change the most exciting
and challenging. Change is never easy, especially business change. Organi-
zations are too often set in their ways and not willing to move in different
directions to meet evolving customer demands. IT is really just an enabler
to business change, a tool to make business more effective, more efficient,
and help better serve customers.
Q: What are some of the biggest challenges that organizations you have worked
for are facing in today’s environment? What are they doing about those
challenges?

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

Dynamic Data Structures

Once we have created an array or a string, its size is


fixed. Because of this characteristic, such data struc-
tures are said to be static. We now turn our attention to
dynamic data structures whose size can grow and shrink
during execution of a program, as the needs of the pro-
gram vary.
478 CHAPTER 12. DYNAMIC DATA STRUCTURES

12.1 Linear Lists and Linked Lists

As a first example of a dynamic data structure we will examine a new way


of storing lists of items. It is certainly possible to implement a list using an
array but, for many applications, arrays are inconvenient. If, for example,
we want to keep the items in order based on some key, then insertion or
deletion of an item might require us to move many other elements. Because
such problems arise frequently, list manipulation is one of the most common
areas for use of dynamic data structures. Before we look at new ways of
storing and manipulating lists in a computer, however, it will be useful
to take a closer look at what we mean by a list, without reference to a
computer.
A general definition of a linear list might say that it is a sequence of
nodes together with a set of operations on those nodes. The undefined
term “node” can be thought of as something carrying some information;
it might be a person’s health record, a playing card, or simply an integer.
The essential feature of a linear list is the fact that it is a sequence of
nodes. If there are n ≥ 0 nodes in the list, we can write the sequence as
x1 , x2 , . . . , xn where x1 is the first node, x2 is the second, and so on, with
xn being the last node. The operations that we might want to perform on
a list will vary from one situation to another. They might include
• examine the contents of the node at one end of the list

• insert or delete a node at one end of the list

• insert a new node before or after a node containing some key

• delete the k th node

• alter the contents of the k th node

• search for a node that contains some value

• sort the nodes of the list based on some key

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

defines an abstract data type or ADT. In general, an ADT is a collection of


data together with a set of operations that can be performed on the data.

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).

Once we have defined an ADT that is appropriate for a particular


problem, we can then look at the best way of implementing it. For a
linear list, an obvious way to implement it is to use an array and we have
already seen many instances in which we used arrays for this purpose. As
we noted above, however, there are many applications for which arrays are
not convenient for list implementation.
An alternative that overcomes many of the problems associated with
arrays is the use of linked lists as implementations of linear lists. In a linked
list, each node contains not only the information required for the list but
also a reference to the next node in the list. The structure of a linked list
is shown in the next diagram.

List

head
Node Node Node

- info - info - ppp info


link link link

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

To define such a structure in Java, we can start with the following:


class List
{
private Node head;
}
We want head to be private so that it cannot be manipulated directly
from outside the List class. Notice that the type of head is Node because
it will be used to refer to objects of type Node.
We can start to define the Node class by defining structures for the
data that we are going to store (shown by the info fields in the diagram)
and a reference to the next node in the list (shown by the link fields
in the diagram). The info field will, in practice, usually be a reference
to an object that actually stores the information but, for the purpose of
demonstrating the structure of a linked list, we will simply store integers
in these fields.
We have a bit of a problem in deciding whether the fields of the Node
class should be public or private. If we declare them to be private, then
the head field will not be able to access them directly. On the other hand,
if we make them public, then they can be seen and modified by anybody.
Java provides a nice solution to this problem through the use of inner
classes which are, as the name implies, classes defined within other classes.
Such classes are only visible from within the class in which they are defined;
fields and methods of an inner class are only visible from within the inner
class or its containing class. In the following code, we have added an inner
Node class to our List class. The inner class has a constructor that permits
us to create nodes with given info and link fields.
12.1. LINEAR LISTS AND LINKED LISTS 481

class List
{
private Node head;

class Node
{
int info;
Node link;

Node (int i, Node n)


{
info = i;
link = n;
}
}
}

Having decided on the structure of a linked list, how do we actually


create one? We can create a new, empty list with its head field initialized
to null by using the default constructor of the List class. To create an
empty list called myList, we could write:
List myList = new List();
The result of this statement is illustrated in the next diagram.
myList

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

This process could be continued with a method insertSecond that inserts


a second node in a list. Such a method would have to create a new node
and have the link field of the existing node refer to it. The existing node
itself is referred to by head so the link field of this node has the identifier
head.link. The method could take the following form:

public void insertSecond (int item)


{
head.link = new Node(item,null);
}

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:

public void insertThird (int item)


{
head.link.link = new Node(item,null);
}

This, obviously, is not a great way to proceed. We want some process


that is general and does not involve expressions that get increasingly long
as a list grows. There are a number of solutions to this problem. One
of them is to always add nodes at the beginning of the list rather than
the end. You will be asked to examine another approach in the exercises.
The next example shows a method, for the List class, that could be used
repeatedly to build a linked list, one node at a time, by adding items at
the front of the list.

Example 2
This method adds a node to the front of a linked list.

public void addAtFront (int item)


{
head = new Node(item,head);
}

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

then, after a call of the form


myList.addAtFront(7);
the list would become
myList
List
-
head
Node Node Node

- info 7 - info 4 - info 8


link link link

For many operations on linked lists, we need to “walk” through the


structure, node by node, a process known as a traversal of a list. To do
this, we can use a reference variable that moves along the list, from node
to node. To start, we might write
Node temp = head;
so that temp refers to the first node of the list (if there is one). To get temp
to move from one node to the next, we can write
temp = temp.link;
We show how this works in the next example.

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

public void printList ()


{
for (Node temp = head; temp != null; temp = temp.link)
System.out.println(temp.info);
}
Notice the way that the loop is controlled. If the list is empty, then
temp will have an initial value of null, the expression temp != null will
never be true, the loop will never be entered, and nothing will be printed.
If the list does contain any items, then the initial assignment
temp = head;
makes temp refer to the same object that head is referring to — the first
node in the list.
temp

List

head ?
Node Node Node

- info 3 - info 9 - ppp info 2


link link link

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

- info 3 - info 9 - ppp info 2


link link link

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

2. Rewrite the method printList shown in Example 3 so that, as well


as printing the values in the list, it also prints a message if the list is
empty.
3. Write an instance method sum for the List class that returns the sum
of the values in the info fields of its implicit List object.
12.2. MORE OPERATIONS ON LINKED LISTS 487

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.

12.2 More Operations on Linked Lists

Although the list manipulation techniques discussed in the previous section


are adequate for some applications, many problems require more advanced
techniques, using one or more auxiliary reference variables to keep track of
nodes while links are being adjusted.
As a first example, let us consider the problem of inserting a new node
into a list where the nodes of the list are to be ordered. For the simple
list structure that we are using, let us suppose that we want to maintain
our lists in ascending order by the info fields of the nodes. To illustrate
the problem, consider the portion of the list shown in the next diagram
and suppose that we want to insert a node containing the value 14 into its
correct position.
488 CHAPTER 12. DYNAMIC DATA STRUCTURES

Node
newNode
- info 14
link

Node Node Node Node


info 10 info 13 info 20 info 25
- - -
link link link link

The insertion process consists, essentially, of simply changing the appro-


priate link fields to achieve the structure shown in the next diagram.

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.

public void insert (int item)


{
// Find correct location in list for new item
Node current = head;
Node previous = null;
boolean located = false;
while (!located && current != null)
if (item < current.info)
located = true;
else
{
previous = current;
current = current.link;
}

// Create a new node containing item and


// referring to correct location in list
Node newNode = new Node(item,current);

// set link to refer to new node


if (current == head)
head = newNode;
else
previous.link = newNode;
}

To delete a node containing a particular value in its info field, we


must first search the list to find the node that is to be deleted and then
adjust links so that this node is snipped out and the reference to the deleted
node is redirected to the following node (if there is one). Here again, we
must be careful to keep track of the node preceding the one currently being
examined so that we can adjust its link, if necessary.
490 CHAPTER 12. DYNAMIC DATA STRUCTURES

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.

public void delete (int item)


{
// Search for node to be deleted
Node current = head;
Node previous = null;
boolean found = false;
while (!found && current != null)
if (current.info == item)
found = true;
else
{
previous = current;
current = current.link;
}

// Perform deletion, if item found


if (found)
if (current == head)
head = head.link;
else
previous.link = current.link;
}

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

current temp item


28

@
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.

temp = new Node(current.info,current.link);


current.info = item;
current.link = temp;

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.)

7. Write an instance method isIdentical for the List class. The


method should have the header
boolean isIdentical (List other)
The method should return true if and only if its implicit List object
is identical to other.

12.3 Stacks and Queues

As we said when we introduced the idea of a linear list, different appli-


cations require lists with different operations. It turns out that, for many
applications, insertions and deletions are only required at the ends of the
list. For such applications, it is often more efficient if we use lists that
only have the operations necessary for the problem. In this section, we will
examine two of the most important types of linear lists for which there are
restrictions on the points at which insertions and deletions can be made.

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

Following this analogy, to insert an item into a stack, we push it onto


the stack; to delete an item, we pop it from the stack. Because of this, stacks
are sometimes referred to as pushdown stores. They are also referred to
as LIFO (Last In, First Out) lists because the last item pushed onto the
stack will be the first item popped from it.
To make the concept of a stack as an ADT more formal, we can make
the following definition: a stack is a linear list with the following operations:

• create create a new, empty stack

• push add a new item onto one end of the list (the top)

• pop delete and return the item at the top

• isEmpty return true if and only if the stack is empty

• peek return the value of the item at the top

Java actually supplies a Stack class in the package java.util with


methods for each of these operations but we will not be using it as it would
spoil the fun of creating our own class for implementing this data type.
To implement a stack, we could use a partially-filled array, a process
that we explored in Chapter 8. Here, however, we will examine an alter-
native structure for stack implementation — dynamic allocation using a
linked list. The form of such a linked list is the usual one that we have
been using up to now, with each node consisting of info and link fields.
We often draw a stack as shown in the following diagram to reinforce the
idea that the first node is at the “top” of the stack but, in fact, there is
nothing new in the way that the list is defined.
494 CHAPTER 12. DYNAMIC DATA STRUCTURES

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:

• create create a new, empty queue

• 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

• isEmpty return true if and only if the queue is empty

• peek return the value of the item at the front

To implement a queue, we can again use either sequential allocation


with an array or linked allocation. We will look only at linked allocation in
this section. Because insertions and deletions take place at opposite ends of
the queue, it is useful to have separate references to the front and the rear
of the queue. This is illustrated in the next diagram in which the object at
the top of the diagram is of type Queue.
496 CHAPTER 12. DYNAMIC DATA STRUCTURES

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;

Node (int i, Node n)


{
info = i;
link = n;
}
}
}
To insert a new node in a queue, we must examine two cases. If the
queue is empty, then both front and rear must be set to refer to the new
node. Otherwise, the new node must be inserted after the node referred to
by rear after which rear must be redirected to refer to the newly inserted
node.
12.3. STACKS AND QUEUES 497

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

Railroad cars, numbered 1, 2, 3, . . ., n enter at the right and leave at


the left. As cars enter, we can consider them to be pushed onto the
stack; as they leave, we can consider them to be popped from the
stack. The order in which they leave at the left can be altered (for
a given arrival order) by choosing different sequences of push and
pop operations. For example, if there are three cars, then the initial
order (1, 2, 3) can be changed to 2, 1, 3 by performing the following
sequence of operations: push, push, pop, pop, push, pop.

(a) Find all the possible output sequences that can be produced
from the input sequence 1, 2, 3.

(b) Repeat part (a) for the input sequence 1, 2, 3, 4.

12.4 Recursive List Processing

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

List The tail


head
Node Node Node

- info 3 - info 9 - ppp info 2


link link link

Recursive algorithms for list processing reflect this structure by pro-


cessing the node referred to by the head directly and processing the tail
recursively with an empty list acting as a stopping condition.

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

called with an implicit List object. Unfortunately, the statement


head.link.printList();
attempts to make a call to printList with the object head.link, which
is of type Node. There are a number of ways to get around this problem.
The one that we have chosen is to add a version of printList to our Node
class. In the List class, we use a non-recursive method that checks to see if
the list is not empty and, if it is, the method then calls the recursive helper
version in the Node class. That method is guaranteed to have a non-empty
list so its algorithm is:
To PRINT A NON-EMPTY LIST
print the info field of the first node
if the tail is not empty
PRINT THE NON-EMPTY LIST that forms the tail
The results are shown in the following example.

Example 2
The printList methods shown here illustrate correct recursive printing of
a linked list.
class List
{
private Node head;

public void printList ()


{
if (head != null)
head.printList();
}

class Node // an inner class


{
int info;
Node link;

Node (int i, Node n)


{
info = i;
link = n;
12.4. RECURSIVE LIST PROCESSING 501

void printList ()
{
System.out.println(info);
if (link != null)
link.printList();
}
}
}

Using recursion can simplify the structure of many methods. To illus-


trate this, consider the problem of inserting a new node in a list in which
the item fields are in ascending order (with smallest values at the front of
the list). We solved this problem in Example 1 of Section 12.2 by using two
auxiliary references to the nodes of the list. Recursion allows us to look at
the problem in a much different and simpler way.

Example 3
This is a recursive algorithm for insertion of a node containing item in the
info field in an ordered list.

To INSERT IN THE LIST


if the list is empty or item < first node’s info field
insert a node containing item here
else
INSERT IN THE LIST referred to by the first node

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

2. In Example 4, if we had written the first if statement in the form


if (item < head.info || head == null) ...
then the method would not work correctly. Why?
3. Complete the following outline of the algorithm used by the recursive
version of insert given in the text:
To INSERT IN THE TAIL OF A NON-EMPTY LIST ...
4. Write a class List that implements the printList and insert meth-
ods discussed in this section. Test your methods by writing a main
method that repeatedly prompts a user for non-negative integers us-
ing zero as a sentinel to stop input. The non-zero values should be
inserted, in ascending order, into a linked list. Once the list has been
created, its contents should be printed.
5. Write a pair of methods, similar in structure to the ones shown in
this section, to delete the last node in a linked list.
6. We can use recursion to make a copy of a linked list. To do so, we
could start by writing the method shown here for the List class. It
creates a new List object and then, if necessary, calls a recursive
helper method in the Node class to create the required nodes for the
copy.
public List copy ()
{
List other = new List();
if (head != null)
other.head = head.copy();
return other;
}

Complete the solution by writing the recursive copy method for the
Node class.

12.5 Binary Trees

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

In talking about trees, the terminology is taken partially from family


trees and partially from organic trees. Considering the tree shown in Ex-
ample 1 as a family tree, the nodes F and G are the children of C, with F
being the left child and G being the right child. Since F and G both have
C as a parent, they are siblings. From organic trees, we get the terms root
and leaf. The node A is the root of the tree while H, E, F, and I are the
leaves. (In computer science, trees usually grow upside down.) The root is
the only node that has no parent while the leaves are the only nodes that
have no children.
The nodes B, D, E, and H form the left subtree of the tree while C,
F, G, and I form the right subtree. Each subtree is itself a tree so that, for
example, B, D, E, and H form a binary tree whose root is B.
To implement a binary tree, we can use arrays but, in this text, we
will only be looking at linked allocation. To start a Tree class, we need
one field, a reference to the root of a tree.

class Tree
{
private Node root;
}

We represent objects of type Tree by diagrams like the following:


12.5. BINARY TREES 505

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;

Node (int i, Node l, Node r)


{
info = i;
lChild = l;
rChild = r;
}
}

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

Operations on binary trees are most easily performed using recursion.


As we saw in the previous section, it is useful, if we want to develop recursive
algorithms for processing a data structure, to first look at that structure
recursively. We can do this here by defining the structure of the ADT binary
tree as: a set of nodes that is either empty or, if not empty, contains a node
called the root together with two disjoint binary trees: the left subtree of
the root and the right subtree of the root.

Root

' $
' $
@
@

Left Subtree Right Subtree

& %
& %
12.5. BINARY TREES 507

Recursive algorithms for operating on binary trees reflect this form


by processing the root directly and processing the left and right subtrees
recursively with an empty tree acting as a stopping condition on the recur-
sion.
As a simple illustration of recursion with a binary tree, consider the
problem of performing a traversal of a tree. In a traversal, we want to
visit each node of the tree, performing some task such as printing the
data in each node as we carry out our visitation. If we want to traverse a
binary tree, there are many possible orders in which we can visit the nodes.
(For a tree containing ten nodes, for example, there are 3 628 800 possible
orderings.) Of these many orderings, only a few are commonly used. Three
of the most common orderings are very similar. In each of them, the left
subtree is traversed before the right subtree. The only difference among
the three is the point at which the root is visited. The outlines and names
of the orderings are shown in the table.

Common Binary Tree Traversals

Preorder Inorder Postorder


Root Left Subtree Left Subtree
Left Subtree Root Right Subtree
Right Subtree Right Subtree Root

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.

To TRAVERSE THE TREE


if the tree is not empty
TRAVERSE THE LEFT SUBTREE
visit the root
TRAVERSE THE RIGHT SUBTREE

As we did for recursive operations on linked lists, we break up the task


here into two parts: a non-recursive method in the Tree class to process the
case of an empty tree and a recursive version in the Node class to process
non-empty binary trees.
508 CHAPTER 12. DYNAMIC DATA STRUCTURES

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

(a) an inorder traversal?


(b) a postorder traversal?
(c) a preorder traversal?




64
Z
 
 Z 
Z
 
52 39

 J   J 
J J

   
15 32 26 35

 
 
28 20

3. The diagram shows an expression tree with operands at the leaves


and operators at the other nodes.
510 CHAPTER 12. DYNAMIC DATA STRUCTURES



+
Z

 Z 
Z
 
÷ −

 J   J 
J J

   
8 2 × 6

 J 
J

 
3 8

(a) The value of a non-empty expression tree is obtained by applying


the operator at the root to the value of the left subtree and the
value of the right subtree. Find the value of the given expression
tree.
(b) In what order would the nodes of the tree be visited using
i. a preorder traversal?
ii. an inorder traversal?
iii. a postorder traversal?
(c) An expression tree, visited in postorder, gives the following ex-
pression
2 3 + 5 4 2 − + ×
Draw the expression tree and find its value.

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.

public int sum()


{
if (root == null)
return 0;
else
return root.sum();
}

(a) Write the definition of a recursive method sum for the Node class
that will complete the process.
12.6. BINARY SEARCH TREES 511

(b) What happens if the tree is empty?


(c) Explain why two versions of sum are used.

12.6 Binary Search Trees

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.

An algorithm for performing a search in a non-empty binary search tree


can take the following form.
To SEARCH A NON-EMPTY TREE
if item is at root
search is successful
else if item < root
if left subtree is empty
search fails
else
SEARCH THE NON-EMPTY LEFT SUBTREE
else
if right subtree is empty
search fails
else
SEARCH THE NON-EMPTY RIGHT SUBTREE
As usual, when we implement this algorithm in Java, we use a non-
recursive version (in an outer class) to handle the possibility that the entire
tree is empty and a recursive version (in an inner class). The implementa-
tion is left as an exercise.
To insert a new node in the tree, we follow an algorithm similar to that
used in our search. The recursive part of the algorithm is shown here.
To INSERT IN A NON-EMPTY TREE
if item precedes root
if left subtree is empty
insert as left child of this node
else
INSERT IN NON-EMPTY LEFT SUBTREE
else
if right subtree is empty
insert as right child of this node
else
INSERT IN NON-EMPTY RIGHT SUBTREE
12.6. BINARY SEARCH TREES 513

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);
}

Once we have created a binary search tree, it is very simple to print


the values of the nodes in order by the keys. Since the values in the
left/right subtree of any node precede/follow that root’s values, they should
be printed before/after the value of the root. This is exactly what we get
when we print a binary tree using an inorder traversal.
514 CHAPTER 12. DYNAMIC DATA STRUCTURES

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.

2. Referring to the binary search tree shown in Example 1, which nodes


would be examined if we were to search for the following names in
the tree?
(a) Michael (b) Barry

(c) Nemanja (d) Dennis

3. Referring again to the tree of Example 1, if we were to insert Tina


in the tree, she would go in as the right child of Thomas. Determine
where the following names would be inserted.
(a) Jane (b) Polly

4. A binary search tree is to be created using the names of the planets.


Draw a diagram showing the resulting binary search tree if the items
are inserted in the following order into an initially empty tree.

Mercury Venus Earth Mars Jupiter Saturn Uranus Neptune Pluto

5. (a) What would happen if the insertion methods of Example 2 were


used to attempt to insert an item that was already in the binary
search tree?
(b) How would the insert method of the Node class have to be
modified so that an attempt to insert an item that is already
in the tree would be denied and a warning message would be
produced?

6. Write a pair of instance methods, both called contains and both


having a single int parameter called item, that search for item in a
binary search tree. The methods should return true if and only if
item is found in the tree.
12.7. AVOIDING ERRORS AND DEBUGGING 515

12.7 Avoiding Errors and Debugging

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

1. The order in which reference values are assigned is almost always


crucial to the success of a method involving links. If a method is not
behaving correctly, use diagrams to help you in determining which
references should be changed and in what order those changes should
occur.
2. In attempting to debug a program involving linked data structures,
it is often difficult to tell where the problem is occurring — in the
building of the structure or in the manipulation of a properly con-
structed list or tree. To try to isolate a problem, it may be useful to
build a simple structure “by hand” and then test any manipulation
methods on this list. As an example, a linked list (of our usual type)
containing three nodes could be created by the following constructor
for the List class.

public List (int m, int n, int p)


{
head = new Node(m,null);
head.link = new Node(n,null);
516 CHAPTER 12. DYNAMIC DATA STRUCTURES

head.link.link = new Node(p,null);


}

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?

Node temp = head;


do
{
System.out.println(temp.info);
temp = temp.link;
}
while (temp != null);

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

12.8 Review Exercises 12

1. Determine the effect of the method mystery in the class List.1

public void mystery ()


{
Node p = head;
Node q = null;
Node r;
while (p != null)
{
r = p;
p = p.link;
r.link = q;
q = r;
}
head = q;
}

2. The diagram shows a binary tree.





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

List the nodes of the binary tree in


(a) preorder (b) inorder (c) postorder

3. The diagram shows a binary search tree containing the values 1, 2,


and 3. Draw similar diagrams showing all possible binary search trees
containing only these three values.


2

 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.

Grumpy Happy Doc Dopey Sneezy Sleepy Bashful

5. In the diagram, p is a reference to an object of type Node within the


Tree class. The field marked A can be referred to as p.info. Find
similar names for the fields marked B, C, D, E, and F.

?
Node
info
A
lChild rChild
B

? ?
Node Node
info info
C E
lChild rChild lChild rChild
D F

6. Write an instance method count that returns the number of nodes


in a linked list.
12.8. REVIEW EXERCISES 12 519

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.

8. Write an instance method appendList that has one explicit List


parameter called other. The method should attach copies of the
nodes of other to the last node of the method’s implicit List object.
The method should leave other unchanged.

9. Suppose that items in a linked list are in non-decreasing order. Write


an instance method simplify that deletes any duplicate entries from
a list. If, for example, before calling simplify, a list contains
2 3 3 3 5 8 8 9
then, after simplify has been called, the list should contain
2 3 5 8 9

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.

11. Write a pair of boolean-valued instance methods, both called isInList


and both having a single int parameter called item. Together, the
methods should use the recursive list processing techniques shown in
this chapter to return true if and only if item is in a list.

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

would be printed as: (2:(5:,),(4:(9:,),(7:,)))


(b) Modify your methods so that leaf nodes (those with no children)
are printed in the form <item> rather than (<item>:,). The
output for the tree shown here would then be: (2:5,(4:9,7))

Projects

16. Write a complete program that prints a sorted list of integers by


first reading them and inserting them into a binary search tree and
12.8. REVIEW EXERCISES 12 521

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.

17. Write a program that contains a set of routines for manipulating a


personal phone directory.2 The directory should be maintained as a
binary search tree with each node containing a reference to an object
containing fields for a family name, a given name, an address, and a
telephone number. The tree’s nodes should be ordered using family
names as keys. If there are entries with identical family names, given
names should be used as secondary keys. No entry will have identical
family and given name.
The program should be menu driven with the user being offered a
choice of six commands:
Insert a new entry in the directory. The program should prompt the
user for a new name, address, and telephone number and then insert
the new entry in the tree. If the name is already in the list, an error
message should be produced and the item should not be entered in
the tree.
Delete an entry from the directory. The program should prompt the
user for the name to be deleted and then delete the entry with that
name from the directory. If no entry with that name is found in the
directory, an error message should be produced. With a binary tree,
deletions can be a bit of a problem. One way to solve the problem is
to have a field in each node that indicates whether or not the node
has been deleted. This could be a boolean field that is true if and
only if the node has not been deleted. When a node is created, this
field is marked as true. To “delete” a node, you can simply mark
this field as being false to indicate that the node has been deleted.
Search for a (user-supplied) name in the directory. The program
should print the name, address, and telephone number of the entry
or an error message if the entry is not found in the directory.
Edit an entry. The user should supply a family name and given
name and the program will then prompt the user to provide new
information for the address fields and/or the telephone number field.
2 This problem requires the use of files. Notes on the use of files are given in Ap-
pendix B.
522 CHAPTER 12. DYNAMIC DATA STRUCTURES

Print the list, in alphabetical order by family name, in a file. The


user should be allowed to specify the name of the file.
Quit the program, saving the current directory entries in a file. Given
the Quit command, the program should save the data using a pre-
order traversal of the tree. This will allow you to reconstruct the
tree in the form that it had before being saved. On giving the Quit
command, the user should be able to specify the name of the file
into which the data should be saved. When you quit the program,
you should save only the “non-deleted” nodes. This might disturb
the structure of the tree to some extent but it shouldn’t mess the
structure up too much. On starting the program, the user should
be given the option of either starting with an initially empty tree or
retrieving an existing directory from a file whose name is specified by
the user.
Chapter 13

Graphical User Interfaces

Up to this point, both our input and our output have


been of a very simple nature, with input consisting of
numbers, characters, or strings entered one at a time
from a keyboard and output consisting only of text sent
to the screen — often referred to as console output. Of
course, in the real world, most of our interaction with
computers takes place in very different ways, often using
menus, buttons, mouse activities, and so on. Such activ-
ities are carried out in what is known as a graphical user
interface, usually abbreviated to GUI (and pronounced
“gooey”). In this chapter we begin to explore some of
Java’s tools for creating and manipulating GUI’s.
524 CHAPTER 13. GRAPHICAL USER INTERFACES

13.1 Drawing Text

Java’s Application Programming Interface (API) has an enormous num-


ber of classes containing fields and methods for creating and manipulating
GUI’s. In early versions of Java, these classes formed Java’s Abstract Win-
dowing Toolkit (AWT) but these have, to a large extent, been superseded in
more recent releases of the language by a collection of classes called Swing.
Together, Swing and a version of the AWT that is much expanded from
its original structure, form the Java Foundation Classes (JFC). The JFC
is a huge collection and, in this chapter, we will only scrape its surface but
there is enough material here to enable you to create GUI’s that should be
adequate for a wide variety of programs.
We start our study of GUI’s very much as we did in Chapter 1, with a
program that displays the message “Hello, world”. This time, however, the
message will be contained in a window like those in the system in which
you are working.1

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

JPanel pane = (JPanel) frame.getContentPane();


pane.add(new Greeting());
frame.setVisible(true);
}
}

class Greeting extends JComponent


{
public Greeting ()
{
repaint();
}

public void paint (Graphics g)


{
g.drawString("Hello, world",150,50);
}
}

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

compiler that we will be using certain classes in other packages. If we


did not use import, we would have to specify the full name of any class
outside java.lang. For example, without import, the statement
JFrame frame = new JFrame();
would have had to be rewritten as
javax.swing.JFrame frame = new javax.swing.JFrame();

2. public static void main (String[] args)


When we run our program, it starts, as usual, by executing the code
of the main method. The purpose of our main method is to set up a
window to display the output of the program.

3. JFrame frame = new JFrame("Graphical Greeting");


The main method starts to create the window by constructing a
JFrame object that we have called frame. A JFrame object acts as
a top-level container for other objects. The string argument of the
JFrame constructor appears in the title bar that runs along the top
of the window.

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.

5. JPanel pane = (JPanel) frame.getContentPane();


Frames do not handle the responsibility for managing the contents
of a window directly. Instead, this is delegated to a content pane,
an object that is automatically associated with each frame. Here,
the call to the getContentPane method in the JFrame class is used
to assign to the identifier pane a reference to frame’s content pane.
(The getContentPane method returns a reference to a Container
object; we can cast this to a JPanel object reference because JPanel
is a subclass of JComponent which is a subclass of Container.)

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

execution of a new, separate process (or thread) that operates in response


to events such as mouse clicks, button presses, and so on. We refer to pro-
grams that operate in response to events as being event driven. Discussion
of events and responses to them will be one of the primary topics of this
chapter.
As our program stands, it has one major problem. Even when we
close the window, the program keeps running! To terminate the program,
we must do whatever our system requires. You might be able to do this by
pressing <ctrl>C but the actions necessary to kill a program vary from one
environment to another. If we want the program to stop when the window is
closed, we must explicitly add a feature that allows the program to respond
to the window-closing event by halting the thread. The procedure for doing
this involves the use of a number of features that we have not yet seen. We
give the code here (so that you can write programs that terminate) but we
will defer our discussion of it until later in the chapter.

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

do this by first constructing a Font object, passing it a font, style, and


size. The basic choices for font are "Serif" (to produce text like this),
"SansSerif" (to produce text like this), and "Monospaced" (to produce
text like this). The style should be one of the constants Font.PLAIN,
Font.BOLD, Font.ITALIC, or Font.BOLD+Font.ITALIC. The size is speci-
fied in points. To give you a feeling for point measurements, the size of this
text should be (about) ten points. (We say “about” because, at the time
that this was written, the final style of the text had not been determined.)
A Font object can be used in drawing by invoking the setFont method
from the Graphics class to apply the font to a Graphics object, as shown
in the next example.

Example 3
The following fragment could be used in a paint method to draw the word
tiny.

Font smallMonoFont = new Font("Monospaced",Font.PLAIN,6);


g.setFont(smallMonoFont);
g.drawString("tiny",50,50);

One other way in which we can improve our program is to simplify


the import statements. Rather than writing an import statement for each
class that we want to use (outside the package java.lang), we can use an
asterisk as a “wild card” character to say, in effect, that we want to be able
to use any class in a particular package. For our example, we can replace
the statements
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Container;
import javax.swing.JComponent;
import java.awt.Graphics;
import java.awt.event.WindowEvent;
with the statements
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
530 CHAPTER 13. GRAPHICAL USER INTERFACES

In the next example, we again greet the world. Now, however, we


incorporate a simplified set of import statements, code to terminate the
program when the window is closed, and a more impressive font for our
salutation.

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);
}
}

class BigGreet extends JComponent


{
public BigGreet ()
{
repaint();
}
13.1. DRAWING TEXT 531

public void paint (Graphics g)


{
Font largeSerifFont = new Font("Serif",Font.PLAIN,40);
g.setFont(largeSerifFont);
g.drawString("Hello, world",100,50);
}
}

The window produced by this program should look like this.

Exercises 13.1
1. How would you set the title of a window to be Sample?

2. Rewrite the lines that would have to be changed in the program of


Example 1 if the import statements were omitted.

3. In Example 1, explain what would happen if the call to drawString


were changed to
g.drawString("Hello world",0,0);

4. What changes would have to be made in Example 4 to produce a


greeting that was
(a) italicized? (b) bold and italicized?

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

13.2 Drawing Simple Shapes

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).

Rectangles and squares can be drawn in outline using drawRect or as


solid regions using fillRect. Both methods have four int parameters:
the first two give the coordinates of the upper left corner of the rectangle
and the last two give the width and height respectively.

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

at a point 200 − 25 = 175 pixels from the left.

To draw polygons (in outline or filled) we can use drawPolygon or


fillPolygon. Both methods have three parameters of the form (int x[],
int y[], int n) The arrays x and y specify the coordinates of the vertices
of the polygon while n specifies the number of vertices. The polygon is
formed using line segments joining (in order) the points specified by the
arrays. The polygon is automatically closed by joining the last point to
the first (if they are different). The line segments can cross. If they do,
fillPolygon fills the inside of the resulting figure in a reasonable way.

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);

int[] xQuad = {200,320,300,220};


int[] yQuad = { 20, 40, 10, 50};
g.fillPolygon(xQuad,yQuad,4);
Here are the resulting drawings.
13.2. DRAWING SIMPLE SHAPES 535

Unless you have specified otherwise, drawing is done in black. To get


other colours, you can use objects from the class java.awt.Color. A colour
in Java can be defined by an RGB colour specification scheme in which a
colour is represented by three integers with a range of 0 to 255 indicating
the amount of each of the three primary colours (red, green, and blue) in
the given colour. The Color class contains a number of predefined objects
whose identifiers are shown in the following table.
Predefined Color Objects
Color.red Color.pink
Color.orange Color.cyan
Color.yellow Color.white
Color.green Color.lightGray
Color.blue Color.darkGray
Color.magenta Color.black

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);

To draw something in a specified colour, we can use the instance


method setColor from the Color class. This method can be used with
either a predefined Color object or one that we have defined.

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.

public void paint (Graphics g)


{
536 CHAPTER 13. GRAPHICAL USER INTERFACES

g.setColor(Color.magenta);
g.fillOval(0,0,100,50);

Color lime = new Color(128,255,0);


g.setColor(lime);
g.drawString("An Ellipse",25,30);
}

Exercises 13.2

1. If a region is 300 pixels wide and 200 pixels high, determine the
coordinates of each point.

(a) the upper left corner

(b) the midpoint of the bottom

(c) the centre of the left half

(d) the centre of the lower right quadrant

2. Write a paint method that could be used to draw a square each of


whose sides are 100 pixels long. The square should be positioned so
that it would be at the centre of a window that is 400 pixels wide
and 200 pixels high. The top and bottom lines defining the square
should be green while the sides should be red.

3. Write a paint method that could be used to draw a standard, octag-


onal stop sign that is 200 pixels wide.

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

(200,100) and the length of each of its sides is 20 pixels.

5. Write a complete program containing a paint method that draws


a bull’s eye with a red centre circle, an orange ring around that, a
yellow ring around that, and a green ring around that. The radius
of the centre circle and the width of each of the surrounding rings
should be 10 pixels. The bull’s eye should be located in the centre of
a square region that is 100 pixels wide and 100 pixels high.

13.3 Layout Managers

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);
}
}

In the program, the layout style of pane is set by the statement


pane.setLayout(new FlowLayout());
The statement is not actually needed because FlowLayout is the default
layout manager for a JPanel but inserting it does no harm and may serve
as a useful reminder to readers of the program.

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.

If we resize the window to make it narrower, the positions of the buttons


540 CHAPTER 13. GRAPHICAL USER INTERFACES

are changed automatically, as shown here.

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

JPanel pane = (JPanel) frame.getContentPane();


pane.setLayout(new GridLayout(3,2));
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);
}
}

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

Here is what it produces.

Of course, layout managers can place components other than buttons


on the screen. The next example combines buttons with a graphical display.
It also shows how we can control the positioning of items relative to the
current size of the window.

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);
}
}

class Picture extends JComponent


{
public Picture ()
{
repaint();
}

public void paint (Graphics g)


{
g.setColor(Color.blue);
g.fillOval(getWidth()/4,getHeight()/4,
getWidth()/2,getHeight()/2);
}
}

Here is the resulting image.


13.3. LAYOUT MANAGERS 547

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.

Example 5 also illustrates a new way of controlling the appearance of


an image in a region. The positioning and dimensions of the ellipse are con-
trolled by the methods getWidth and getHeight in the class Component.
The ellipse is centred both horizontally and vertically, with its contain-
ing rectangle being half the width and height of the region in which it
is displayed. Whenever a user resizes the window, the paint method is
called automatically and the ellipse is redrawn, repositioned and resized
appropriately.

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)

(c) new GridLayout(4,0) (d) new GridLayout(0,6)

2. Write a program that will produce the following image in a window


that is 140 pixels wide and 70 pixels high.

3. Write a program that will produce the following display in a square


548 CHAPTER 13. GRAPHICAL USER INTERFACES

window whose sides are 200 pixels in length.

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.

13.4 Button Events

In addition to creating displays, we would also like to be able to construct


GUI’s that are capable of interacting with a user. In this and the follow-
ing sections we will be examining a variety of ways that we can create
interactions with a GUI. We begin with the use of buttons.
Whenever a button is pressed, this produces an event known as an
action event. We say that the button object is the source of the event. If
some object is interested in knowing that a particular event has occurred,
that object must indicate that it wants to be a listener to occurrences of
that event. To do so, the object must register as a listener. Each object that
can produce events maintains a list of the objects that are listeners to that
particular type of event. When an event occurs, all of the objects registered
as listeners for that type of event are passed appropriate information about
13.4. BUTTON EVENTS 549

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

2. We must now actually perform this implementation of the Action-


Listener interface. Recall that to implement an interface, we must
provide actual methods for all the methods whose headers are given
in the interface. The ActionListener interface requires the imple-
mentation of only one method:
public void actionPerformed (ActionEvent e)
It is within this method that we would place our code for the action
we want to perform whenever demoButton is pressed.

3. Finally, we need to add a ButtonResponse object to demoButton’s


list of listeners by invoking the method with header
void addActionListener (ActionListener al)
The implicit object passed to this method is demoButton and the
parameter is a ButtonResponse object (which we have made into
an ActionListener object through the use of the interface). If we
are calling the method from within the ButtonResponse class, the
ActionListener object that we pass to the method is the current
implicit ButtonResponse object. The call, therefore, would take the
form
demoButton.addActionListener(this);

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 class ButtonColour


{
public static void main(String[] args)
{
ColourFrame frame = new ColourFrame();
frame.addWindowListener
(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
}
);
}
}

class ColourFrame extends JFrame implements ActionListener


{
private JPanel pane;

public ColourFrame ()
{
super("Button Events");

JButton colourButton = new JButton("Change Colour");


colourButton.addActionListener(this);

pane = (JPanel) getContentPane();


pane.setLayout(new FlowLayout());
13.4. BUTTON EVENTS 551

pane.add(colourButton);

setSize(300,100);
setVisible(true);
}

public void actionPerformed (ActionEvent e)


{
pane.setBackground
(new Color(strength(),strength(),strength()));
}

public static int strength ()


{
return (int)(256*Math.random());
}
}
The window produced by the program should look something like this.
Each time that the button is pressed, the colour of the pane behind the
button changes randomly to one of 16 777 216 possible colours.2

There are a number of new features in this program.


1. super("Button Events");
The frame of the window is an object of the class ColourFrame,
a subclass of JFrame. The first statement in the constructor of
ColourFrame calls a constructor of the JFrame superclass to con-
struct a frame with the title "Button Events".
2. JButton colourButton = new JButton("Change Colour");
colourButton.addActionListener(this);
2 Since
there are 256 possible values for each of the component colours, the total
number of colours is 2563 = 16 777 216.
552 CHAPTER 13. GRAPHICAL USER INTERFACES

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.

3. pane = (JPanel) getContentPane();


pane.setLayout(new FlowLayout());
pane.add(colourButton);
These statements create a content pane and add the button to it.

4. setSize(300,100);
setVisible(true);
Finally, the ColourFrame constructor sets an initial size for the win-
dow and makes it visible.

5. public void actionPerformed (ActionEvent e)


There is no call to the method actionPerformed. Instead, since Co-
lourPane objects are registered as listeners to colourButton events,
a call to the actionPerformed method within the ColourPane class
is made automatically whenever the button called colourButton is
pressed.

6. public static int strength ()


This method generates a random value in the range 0 to 255, to be
used to control the strength of a primary colour component when a
background colour is generated.

The parameter of the method actionPerformed is an ActionEvent


object called e. This parameter, supplied automatically by Java, contains
information about the event that produced the call to actionPerformed.
Since our program is only registered as a listener for one event (the pushing
of colourButton), we do not need to use the parameter because the only
time that this method will be called is when that particular button is
pushed. If we have registered for more than one event, then we may need
to examine the parameter to determine which event caused the call to
actionPerformed. This is illustrated in the next example.

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

containing a main method as it would be virtually identical to the one


shown in Example 1.

class TriColourFrame extends JFrame implements ActionListener


{
private JPanel pane;

public TriColourFrame ()
{
super("Tricolour Buttons");

JButton redButton = new JButton("red");


redButton.addActionListener(this);
JButton greenButton = new JButton("green");
greenButton.addActionListener(this);
JButton blueButton = new JButton("blue");
blueButton.addActionListener(this);

pane = (JPanel) getContentPane();


pane.setLayout(new FlowLayout());
pane.add(redButton);
pane.add(greenButton);
pane.add(blueButton);

setSize(300,100);
setVisible(true);
}

public void actionPerformed (ActionEvent e)


{
if (e.getActionCommand().equals("red"))
pane.setBackground(Color.red);
else if (e.getActionCommand().equals("green"))
pane.setBackground(Color.green);
else if (e.getActionCommand().equals("blue"))
pane.setBackground(Color.blue);
else
pane.setBackground(Color.black);
}
}
554 CHAPTER 13. GRAPHICAL USER INTERFACES

In Example 2, the actions of the constructor in the class TriColour-


Frame are similar to those of the corresponding constructor in the previous
example. The only essential difference is the extra work that must be done
because there are now three buttons instead of one.
The actionPerformed method uses the value of the parameter e to
decide what action should be taken. Associated with each button that we
create is an action command. For a JButton, the default value of the ac-
tion command is the string that we use as the label for the button. The
getActionCommand method returns the action command string associated
with an event object. The actionPerformed method in the example uses
this string to determine which button was pressed and then takes the ap-
propriate action.

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?

2. In the actionPerformed method of Example 2, explain why it would


be wrong to start the if statement with
if (e.getActionCommand == ’red’)

3. Write a program that uses a flow layout to display a window contain-


ing two buttons, one labelled "On" and the other labelled "Off". If a
user presses "On", the background colour should be set to white but
if a user presses "Off", the background should be set to black.
13.5. MOUSE EVENTS 555

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.

5. We noted in the text that the default value of a button’s action


command is the string that appears on the button but it is possible
to alter that using the setActionCommand method. To set the action
command of myButton to "belly", we could write
myButton.setActionCommand("belly");
While it is a bit more work, this is probably a good idea for at least
two reasons: the action command can be longer and more descriptive
than the button’s label and, if somebody decides to alter the look of
the button at some point, the action command will not change and
the program will still work. Rewrite the tricolour program of Example
2 so that the action commands are "Make background red", "Make
background green", and "Make background blue" but the button
displays are in French (showing "rouge", "vert", and "bleu").

13.5 Mouse Events

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

• public void mouseReleased (MouseEvent e)


This is called when a mouse button is released within the component
to which we are listening.
• public void mouseClicked (MouseEvent e)
This is called when the mouse is clicked (a press followed quickly by
a release) within the component to which we are listening.
• public void mouseEntered (MouseEvent e)
This is called when the mouse cursor enters the bounds of the com-
ponent to which we are listening.
• public void mouseExited (MouseEvent e)
This is called when the mouse cursor exits the bounds of the compo-
nent to which we are listening.
Implementation of the MouseMotionListener interface requires only two
methods.
• public void mouseMoved (MouseEvent e)
This is called when a mouse is moved, without a button being pressed,
over the component to which we are listening.
• public void mouseDragged (MouseEvent e)
This is called when a mouse is moved, with a button held down,
over the component to which we are listening. If a mouse is dragged
over more than one component, the events produced by dragging the
mouse are delivered to the component where the dragging originated.
The MouseEvent object e that appears as a parameter of each of these
methods is supplied automatically by Java. It can be used to get a great
deal of useful information about the event. We will only be using two
instance methods in the MouseEvent class:
• public int getX ()
This method returns the horizontal coordinate of the mouse when
the event occurred.
• public int getY ()
This method returns the vertical coordinate of the mouse when the
event occurred.
In both cases, the coordinates are given relative to the component involved
in the event. Thus, clicking the mouse at the upper left hand corner of a
13.5. MOUSE EVENTS 557

component will produce an event with coordinates (0, 0) no matter where


the component is located on the screen.
Other methods are available in the MouseEvent class to indicate which
mouse button was involved in the event, to give the identifier of the com-
ponent that was clicked, to indicate whether or not a double click had
occurred, and so on. These are all beyond the scope of this book.
To show how mouse events work in a program, let us create a very
simple drawing program that paints a small black circle at any point at
which we click the mouse within a window.

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.*;

public class MouseCircles


{
public static void main(String[] args)
{
CircleFrame frame = new CircleFrame();
frame.addWindowListener
(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
}
);
}
}

class CircleFrame extends JFrame implements MouseListener


{
private int x;
558 CHAPTER 13. GRAPHICAL USER INTERFACES

private int y;
private static final int RADIUS = 10;

public CircleFrame ()
{
super("Mouse Droppings");
addMouseListener(this);
setSize(300,100);
setVisible(true);
}

public void mouseClicked (MouseEvent e)


{
x = e.getX();
y = e.getY();
repaint();
}

public void mousePressed (MouseEvent e)


{
}

public void mouseReleased (MouseEvent e)


{
}

public void mouseEntered (MouseEvent e)


{
}

public void mouseExited (MouseEvent e)


{
}

public void paint (Graphics g)


{
g.fillOval(x-RADIUS,y-RADIUS,2*RADIUS,2*RADIUS);
}
}
13.5. MOUSE EVENTS 559

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.

There are a number of points that should be noted in this program.


1. public static void main (String[] args)
The main method, as usual, does little except set up the basic features
of the window that will display the output.
2. class CircleFrame ... implements MouseListener
The implements clause promises that the CircleFrame class contains
all the methods of the MouseListener interface.
3. private int x;
private int y;
These fields are the coordinates of the centres of the circular regions
that the program draws.
4. addMouseListener(this);
The current implicit component object should be added to the list of
objects that act as listeners to mouse clicks anywhere within the cur-
rent component object. In other words, we want the region to listen
to events occurring within itself! We could achieve this by writing
this.addMouseListener(this);
but the first this is not necessary because, if we omit an object refer-
ence in a call to an instance method, the current object is understood
to be the implicit object in the method call.
5. public void mouseClicked (MouseEvent e)
The mouseClicked method obtains the coordinates of the location
of any mouse click (within the current component’s area) from the
MouseEvent object e and then asks Java to schedule the component
for painting.
6. mousePressed, mouseReleased, mouseEntered, mouseExited
Although we do not want to take any action for mouse pressing,
560 CHAPTER 13. GRAPHICAL USER INTERFACES

releasing, and so on, we must include all of these methods in order


to implement the interface. To satisfy these requirements here, we
have supplied methods (sometimes called stub methods) whose bodies
contain no statements.

7. public void paint (Graphics g)


The paint method simply draws a filled circular region with radius
given by RADIUS and centre at the most recent mouse click.
To save a lot of writing when dealing with listeners that have more
than one method, Java has a number of adapter classes that implement
listener interfaces in a trivial way, with stub methods. We can then create
a listener by extending the adapter class with our own handler class that
overrides the methods that we need with something useful and leaves the
other methods alone.
To illustrate, consider the mouse listener in Example 1. Java has a
class called MouseAdapter that contains trivial versions of all five meth-
ods needed by a mouse listener. The form of each of the methods in
MouseAdapter is the same as four of our methods — a header with an
empty body. If we extend this class and provide a useful mouseClicked
method, we will have everything that we need.
As you might have expected, there are some small difficulties that we
must overcome to use this idea. First, we cannot have our CircleFrame
class extend the MouseAdapter class as it is already descended from the
class JFrame and, in Java, a class can only extend one other class (although
it can implement many interfaces). Therefore, we create a new class as our
handler and let that class extend MouseAdapter.
Creating a separate class, however, creates a new problem. We want
the mouseClicked method to be able to refer to the x and y fields of the
CircleFrame class and to be able to use repaint to activate the paint
method that is also in the CircleFrame class. To overcome this problem,
we can create our handler class as an inner class of the CircleFrame class.
As we saw in Chapter 12, inner classes have exactly the properties that we
want here. The next example incorporates these ideas.

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.

class CircleFrame extends JFrame


{
13.5. MOUSE EVENTS 561

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);
}

class ClickHandler extends MouseAdapter


{
public void mouseClicked (MouseEvent e)
{
x = e.getX();
y = e.getY();
repaint();
}
}

public void paint (Graphics g)


{
g.fillOval(x-RADIUS,y-RADIUS,2*RADIUS,2*RADIUS);
}
}

Now, we no longer have the clause implements MouseListener in the


header of either of our classes. This is because the implementation of
MouseListener is done in the MouseAdapter class. The constructor of the
CircleFrame class has also been changed; it now calls the constructor of
the ClickHandler class to create a new ClickHandler object and then
puts that object on the mouse listener’s list of objects to be informed of
mouse events.

We can carry the process of cutting down on writing even further


(possibly at the risk of producing code that is hopelessly confusing). In
Example 2, the constructor method of the CircleFrame class did not give
562 CHAPTER 13. GRAPHICAL USER INTERFACES

an identifier to the ClickHandler object because we only made one refer-


ence to it. Following this line of logic, we should not need an identifier for
the handler class because we only instantiate it once. The designers of Java
allow this to happen by permitting the use of anonymous inner classes as
shown in the next example.

Example 3
The following code uses an anonymous inner class to install a mouse listener
in the class CircleFrame.

class CircleFrame extends JFrame


{
private int x;
private int y;
private static final int RADIUS = 10;

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);
}

public void paint (Graphics g)


{
g.fillOval(x-RADIUS,y-RADIUS,2*RADIUS,2*RADIUS);
}
}
13.5. MOUSE EVENTS 563

We interpret the argument of the call to the addMouseListener method


as follows: Construct an object of the class that extends the class Mouse-
Adapter and give it a mouseClicked method that overrides the one that
exists in MouseAdapter.

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.

(a) mousePressed and mouseClicked


(b) MouseListener and MouseMotionListener
(c) MouseListener and MouseAdapter

2. Although the designers of Java created adapter classes for Mouse-


Listener and MouseMotionListener, they did not do so for Action-
Listener. Do you think that this was an oversight, sheer laziness, or
something else? Justify your answer.
564 CHAPTER 13. GRAPHICAL USER INTERFACES

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.

13.6 Text Input and Output

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);

If a field is only to be used for output, we can prevent a user from


using the field for input by making the field non-editable.

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

When displayed, outField will have an appearance that is different from


fields that can be edited.

To work with the contents of a JTextField, we can convert the value


to a string using the getText method. To get rid of extra white space that
the user may have inserted at either the beginning or end of the input, it
is a good idea to use the trim method of the String class.

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();

If a JTextField object represents a numerical value, we can obtain


that value using methods available in the wrapper classes associated with
each primitive numerical type. We saw wrapper classes earlier when we
used them to enclose primitive types in objects. The classes also have a
number of utility methods for type conversions. The methods that we are
interested in are those that convert strings to the appropriate numerical
value. These are all class methods that have the form parse<something>.
The table shows the headers of these methods and the classes that contain
them.

Class Conversion Method


Byte public static byte parseByte (String s)
Short public static short parseShort (String s)
Integer public static int parseInt (String s)
Long public static long parseLong (String s)
Float public static float parseFloat (String s)
Double public static double parseDouble (String s)
13.6. TEXT INPUT AND OUTPUT 567

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.

If the argument of a parse... method is not a string of the appropriate


form, then Java throws a NumberFormatException at execution time. If
the programmer does not catch such exceptions, the program will crash.3
To display values in a JTextField that is being used for output, we
must make conversions in the opposite direction. As we have seen before,
the String class contains an overloaded class method valueOf that can
be used to convert the value of any primitive type or object to a string.
Values of strings can be converted to JTextField values using the method
setText, as shown in the next example.

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.

To identify a JButton, we had a label that appeared on the button.


To label a JTextField, we usually use some text in an area adjacent to the
field. In order to do this, we can create a JLabel object that can display
either text, an image, or both. A label does not react to input events. Text
labels are, by default, located at the left side of their display area, centred
vertically.
3 Appendix E contains information on handling exceptions without causing execution
to terminate.
568 CHAPTER 13. GRAPHICAL USER INTERFACES

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.*;

public class TextFields


{
public static void main(String[] args)
{
NameFrame frame = new NameFrame();
frame.addWindowListener
(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
13.6. TEXT INPUT AND OUTPUT 569

System.exit(0);
}
}
);
}
}

class NameFrame extends JFrame implements ActionListener


{
private JTextField givenName;
private JTextField familyName;
private JTextField fullName;

public NameFrame ()
{
super("Text Fields");

// Organize content pane


JPanel pane = (JPanel) getContentPane();
pane.setLayout(new BorderLayout());

// Create fields for I/O


givenName = new JTextField(10);
familyName = new JTextField(10);
fullName = new JTextField(10);
fullName.setEditable(false);

// Add labelled input fields to display


JPanel inFieldPane = new JPanel();
inFieldPane.setLayout(new GridLayout(2,2));
inFieldPane.add(new JLabel("Given Name"));
inFieldPane.add(givenName);
givenName.addActionListener(this);
inFieldPane.add(new JLabel("Family Name"));
inFieldPane.add(familyName);
familyName.addActionListener(this);
pane.add(inFieldPane,BorderLayout.NORTH);

// Add submission button


JPanel submitPane = new JPanel();
570 CHAPTER 13. GRAPHICAL USER INTERFACES

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);

// Add Output fields


JPanel outFieldPane = new JPanel();
outFieldPane.setLayout(new GridLayout(1,2));
outFieldPane.add(new JLabel("Full Name"));
outFieldPane.add(fullName);
pane.add(outFieldPane,BorderLayout.SOUTH);

// Display the final product


pack();
setVisible(true);
}

public void actionPerformed (ActionEvent e)


{
// Display full name if and only if button was pushed
if (e.getActionCommand().equals("Submit"))
{
String fullString = familyName.getText().trim() + ", "
+ givenName.getText().trim();
fullName.setText(fullString);
}
}
}
Here is a typical window produced by the program.
13.6. TEXT INPUT AND OUTPUT 571

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?

2. What changes would be needed in the actionPerformed method of


Example 7 in order to have the full name updated every time either
the given name or the family name was updated and the <enter> key
was pressed?

3. Write a program that displays a window showing a labelled text input


field at the bottom of the screen and a large blank area above this.
The label should prompt the user to enter a positive integer. After the
value is entered and the <enter> key is pressed, the program should
draw that number of small solid circular regions in the window. The
circular regions should be located at randomly chosen points in the
window and should be drawn in randomly selected colours.

4. Write a program that will produce a window that can be used as


a simple calculator. The window should have two input fields and
one output field plus buttons for each of the operations +, -, *, and
/. The illustration shows the window that would be produced by
572 CHAPTER 13. GRAPHICAL USER INTERFACES

calculating 7 × 5.

13.7 Avoiding Errors and Debugging

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.

2. In implementing an interface, be sure to include all required methods


with exactly the right parameters. If anything is missing or wrong,
the compiler will complain.

Debugging

1. If a component is not responding as it should, check that you have


registered the component as a listener to the appropriate action. For
example, if a button is not responding to mouse clicks, check to see if
you have implemented the ActionListener interface for that button
object. If the component is not registered as a listener, then its
actionPerformed method will not be executed.
13.8. REVIEW EXERCISES 13 573

13.8 Review Exercises 13

1. Assuming that g is a Graphics object, state the error in each state-


ment.

(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:

• In the upper left quadrant, the name Joanne, in a monospaced


font, inside a solid blue circle
• In the lower left quadrant, the name Man-Kwan, in a serif font,
inside an outlined red square
• In the upper right quadrant, the name John, in a sans serif font,
inside a solid magenta rectangle
• In the lower right quadrant, the name Kevin, in an italic font,
inside an outlined cyan ellipse
• In the centre, the name Vaughn, in a bold italic font, inside a
solid yellow isosceles triangle

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.

4. Write a program that allows a user to click on three non-collinear


points in a window and then draws the circle that passes through
those points. If the user selects three collinear points, the program
should draw a message that indicates this.

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

representation of the given time as it would appear on an analog


clock. The “clock” should consist only of a circular region containing
two line segments — a short one for the hour hand and a longer one
for the minute hand.

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

The value c is in the Mandelbrot set if and only if the terms of


the sequence never get “large”. To determine whether or not this
sequence will ever get large, look at up to thirty terms, testing each
one to see whether or not |zk | > 2. (In geometric terms, this checks
to see if the point lies outside the circle of radius 2, centred at the
origin.) If any term of the sequence ever satisfies this condition, the
sequence is guaranteed to get large. (What we mean by this is that,
as k gets large, the value of zk is guaranteed to eventually become
larger than any given value you care to name. As an example, the
sequence 1, 2, 4, . . . gets large in this sense.) If the sequence has not
produced a point that is more than two units from the origin after
thirty iterations, you can depend on it never growing large. In this
case, the point c is part of the graph of the Mandelbrot set on an
Argand diagram. If a point is in the set, use the drawOval method
to add it to the set, as in the previous question. The domain of your
graph should be from -2 to 1 (along the real axis) while the range
should be from -1 to 1 (along the imaginary axis).

9. Write a program that allows two people to play tic-tac-toe against


each other. The program should present a square area that is divided
into nine smaller squares by vertical and horizontal lines. If a user
clicks the mouse in an empty region, the program should place a large
X or a large O (on alternate turns) into the middle of that area. The
program should not permit any move after a player has won the game.
The window should also have a "Restart" button at the bottom to
allow users to start a new game at any time.

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

The Classes In and Out

Both In and Out, shown here and available on our web


site, are not part of the standard Java API. The methods
of In can be used to simplify reading of either primitive
types or strings from the standard input device. While
the print and println methods provided by Java in
the System class make output to the standard output
device quite simple, the methods of Out provide more
control of the format of the output. For those who either
wish to avoid the use of user-defined classes for input
and output or need to perform more complex input and
output operations, Appendix B examines techniques for
performing reading and writing using only the methods
provided in Java’s API.
578 APPENDIX A. THE CLASSES IN AND OUT

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);

// Read a String from the standard system input


public static String getString ()
{
try
{
return br.readLine();
}
catch(Exception e)
{
return "";
}
}

// Read a number as a String from the standard system input


// and return the number
public static Number getNumber ()
{
String numberString = getString();
try
{
numberString = numberString.trim().toUpperCase();
return NumberFormat.getInstance().parse(numberString);
}
catch(Exception e)
579

{
// if any exception occurs, just return zero
return new Integer(0);
}
}

// Read an int from the standard system input


public static int getInt ()
{
return getNumber().intValue();
}

// Read a long from the standard system input


public static long getLong ()
{
return getNumber().longValue();
}

// Read a float from the standard system input


public static float getFloat ()
{
return getNumber().floatValue();
}

// Read a double from the standard system input


public static double getDouble ()
{
return getNumber().doubleValue();
}

// Read a char from the standard system input


public static char getChar ()
{
String s = getString();
if (s.length() >= 1)
return s.charAt(0);
else
return ’\n’;
}
}
580 APPENDIX A. THE CLASSES IN AND OUT

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 integer values (including characters), a field width can be specified


by a second argument. The output will be right-justified in the field.
For example,
Out.println(23,5);
will print
23
where denotes a space.
The second argument actually specifies a minimal field width; if the
number requires more space than the specified field width, it will be
printed in whatever space is required.

• 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 class Out


581

{
public static void println ()
{
System.out.println();
}

public static void print (double n, int fieldWidth,


int decimalPlaces)
{
String format = "0.";
for (int i = 0; i < decimalPlaces; i++)
format = format + "0";
NumberFormat form = new DecimalFormat(format);
String s = form.format(n);
int stop = fieldWidth - s.length();
for (int i = 0; i < stop; i++)
s = " " + s;
System.out.print(s);
}

public static void println (double n, int fieldWidth,


int decimalPlaces)
{
print(n, fieldWidth, decimalPlaces);
System.out.println();
}

public static void print (double n)


{
System.out.print(n);
}

public static void println (double n)


{
print(n);
System.out.println();
}

public static void print (long n, int fieldWidth)


{
582 APPENDIX A. THE CLASSES IN AND OUT

String s = String.valueOf(n);
int stop = fieldWidth - s.length();
for (int i = 0; i < stop; i++)
s = " " + s;
System.out.print(s);
}

public static void println (long n, int fieldWidth)


{
print(n, fieldWidth);
System.out.println();
}

public static void print (long n)


{
System.out.print(n);
}

public static void println (long n)


{
print(n);
System.out.println();
}

public static void print (String s, int fieldWidth)


{
int stop = fieldWidth - s.length();
if (s.length() > fieldWidth)
s = s.substring(0, fieldWidth);
else
for (int i = 0; i < stop; i++)
s = s + " ";
System.out.print(s);
}

public static void println (String s, int fieldWidth)


{
print(s, fieldWidth);
System.out.println();
}
583

public static void print (String s)


{
System.out.print(s);
}

public static void println (String s)


{
System.out.println(s);
}

public static void print (char c, int fieldWidth)


{
String s = String.valueOf(c);
int stop = fieldWidth - 1;
for (int i = 0; i < stop; i++)
s = " " + s;
System.out.print(s);
}

public static void println (char c, int fieldWidth)


{
print(c, fieldWidth);
System.out.println();
}

public static void print (char c)


{
System.out.print(c);
}

public static void println (char c)


{
System.out.println(c);
}
}
Appendix B

Input and Output

Reading and writing in Java are not simple processes.


Many classes, related in a variety of ways, are used for
input and output (I/O). Here we examine only a sub-
set of these classes, restricting ourselves to those classes
and methods that will allow us to read and write values
of primitive types and strings. The source for our input
will be either the standard input device (usually the key-
board) or a file; the destination for our output will be
either the standard output device (usually the screen)
or a file. This appendix does not discuss I/O using a
graphical user interface. An introduction to that topic
can be found in Chapter 13.
586 APPENDIX B. INPUT AND OUTPUT

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

InputStreamReader r = new InputStreamReader(System.in);


We can then use a BufferedReader object to collect the characters into
large sequences. This makes reading more efficient. To wrap an Input-
StreamReader in a BufferedReader, we can write
BufferedReader br = new BufferedReader(r);
These two steps can be, and often are, combined into one statement.
BufferedReader br
= new BufferedReader(new InputStreamReader(System.in));

In the BufferedReader class there is a method called readLine that,


as the name suggests, returns a line of data as a string. More precisely, the
method returns a string consisting of all the characters read up to (but not
including) the next newline (’\n’) character.
Both the InputStreamReader class and the BufferedReader class are
found in the package java.io. Any program using them should begin with
an appropriate import statement. For more details about the use of import
statments, see page 368.

Example 1
This fragment prompts for a string and then reads it from the standard
input stream.

InputStreamReader r = new InputStreamReader(System.in);


BufferedReader br = new BufferedReader(r);
System.out.println("Please enter your name");
String name = br.readLine();

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

Class Conversion Method


Byte public static byte parseByte (String s)
Short public static short parseShort (String s)
Integer public static int parseInt (String s)
Long public static long parseLong (String s)
Float public static float parseFloat (String s)
Double public static double parseDouble (String s)
If the string cannot be parsed correctly, then the parsing method throws
a NumberFormatException. This can occur if the string contains either
leading or trailing blanks. These can be eliminated before attempting to
parse the string with the trim method of the String class.

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

Characters and Integers

All information in computers is represented as sequences


of bits. Depending on the context, a given bit pattern
may be interpreted as a number, a character, a string
of characters, an instruction in a computer program,
or something else. In this appendix, we examine some
aspects of the ways that Java stores both characters and
integers as well as some of the relationships between
character and integer encodings.
594 APPENDIX C. CHARACTERS AND INTEGERS

To represent its characters, Java uses a sixteen-bit representation called


Unicode, an encoding system that has representations of virtually all of
the world’s alphabets and ideographic character sets. Unicode characters
in which the first nine bits are zero correspond to the characters of the
seven-bit ASCII1 encoding. In Unicode, this group of characters is known
as the Basic Latin character set.
In addition to representing characters, we can also think of bit patterns
as numbers, written as binary numerals. To explain how such a represen-
tation works, it may be useful to examine how we normally write numbers.

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

Our system for writing numbers is said to be a positional one because


the value represented by each digit depends on the position of the digit in
the numeral. In the example, the value of the digit 2 is 200 while the value
of the digit 5 is 50. We also say that our system is a base 10 or radix 10
system because each position represents some power of ten. In this system,
there are ten digits, 0, 1, . . . , 9. Any non-negative integer in base 10 will
have the form
dn dn−1 . . . d1 d0 = dn × 10n + dn−1 × 10n−1 + · · · + d1 × 101 + d0 × 100
where 0 ≤ di < 10.
There is no reason why we cannot write numbers using a positional
system with a different base. A number expressed as a numeral in base x
is usually denoted by a symbol of the form nx . The structure of such a
number is very similar to that of a number written in our base 10 system.
A non-negative base x integer with digits dn dn−1 . . . d1 d0 represents the
value
dn × xn + dn−1 × xn−1 + · · · + d1 × x1 + d0 × x0
1 American Standard Code for Information Interchange
595

In a base x representation, there are x digits so that 0 ≤ di < x.

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

As we noted above, a base x numeral can have x different digits, rep-


resenting the values 0, 1, . . . , x − 1.

Example 3

(a) Base 5 numerals use the digits 0, 1, 2, 3, 4.

(b) Base 2 numerals use the digits 0, 1.

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

AC3F16 = 10 × 163 + 12 × 162 + 3 × 161 + 15 × 160


= 10 × 4096 + 12 × 256 + 3 × 16 + 15 × 1
= 40960 + 3072 + 48 + 15
= 44095

There is a (possibly) interesting and (certainly) useful relationship


between binary (base 2) representation and hexadecimal (base 16) repre-
sentation. To illustrate this, consider the binary value 1100101001112. To
make it easier to recognize this long string of binary digits, we first rewrite
the number with spaces between every four digits to obtain 1100 1010 0111.
We can then transform the number as follows.

1100 1010 01112 = (1 × 211 + 1 × 210 + 0 × 29 + 0 × 28 )+


(1 × 27 + 0 × 26 + 1 × 25 + 0 × 24 )+
(0 × 23 + 1 × 22 + 1 × 21 + 1 × 20 )
= (1 × 23 + 1 × 22 + 0 × 21 + 0 × 20 ) × 28 +
(1 × 23 + 0 × 22 + 1 × 21 + 0 × 20 ) × 24 +
(0 × 23 + 1 × 22 + 1 × 21 + 1 × 20 ) × 20
= 11002 × 28 + 10102 × 24 + 01112 × 20
= 11002 × 162 + 10102 × 161 + 01112 × 160

This relationship provides us with a means of easily converting num-


bers from a base 2 representation to a base 16 representation. To complete
the conversion, we simply have to write each 4-digit binary value in the
final line as a hexadecimal digit.
597

11002 = 1 × 23 + 1 × 22 + 0 × 21 + 0 × 20 = 8 + 4 + 0 + 0 = 12= C16


10102 = 1 × 23 + 0 × 22 + 1 × 21 + 0 × 20 = 8 + 0 + 2 + 0 = 10= A16
01112 = 0 × 23 + 1 × 22 + 1 × 21 + 1 × 20 = 0 + 4 + 2 + 1 = 7 = 716
These values can then be substituted into the expression 11002 × 162 +
10102 × 161 + 01112 × 160 to obtain our final result.
1100 1010 01112 = C16 × 162 + A16 × 161 + 716 × 160 = CA716
In practice, this procedure is much simpler than it may appear from
what we have done here. To perform such conversions, some people find
it useful to have a table showing the values of all possible groups of four
binary digits.
00002 = 0 = 016 10002 = 8 = 816
00012 = 1 = 116 10012 = 9 = 916
00102 = 2 = 216 10102 = 10 = A16
00112 = 3 = 316 10112 = 11 = B16
01002 = 4 = 416 11002 = 12 = C16
01012 = 5 = 516 11012 = 13 = D16
01102 = 6 = 616 11102 = 14 = E16
01112 = 7 = 716 11112 = 15 = F16
We can now easily convert between binary and hexadecimal represen-
tations. Conversions of both kinds are illustrated in the next example.

Example 5
(a) 10010110101111102 = 1001 0110 1011 11102 = 96BE16
(b) 6F7B016 = 0110 1111 0111 1011 00002
(c) 101001101110102 = 0010 1001 1011 10102 = 29BA16

In Java, we can actually write numbers as hexadecimal numerals by


writing 0x in front of the digits of the number. We can also write numbers
as octal (base 8) numerals by preceding the digits with 0.
598 APPENDIX C. CHARACTERS AND INTEGERS

Example 6
(a) The expression 0x3A2 represents the value 3A216
(b) The expression 057 represents the value 578

These ideas about representation of numbers are related to representa-


tion of characters because Java’s characters are encoded as 16-bit Unicode
patterns which can be converted to integer values. For example, the char-
acter ’÷’ has the representation 0000000011110111. Writing this as a
numerical value gives 0000 0000 1111 01112 = 00F716 . Java provides an
escape character (\u) for writing Unicode characters. To write a character
in this form, we use a string consisting of \u followed by four hexadecimal
digits to represent the 16 bits of the character.

Example 7
The statement
System.out.println(2 + "\u00F7" + 3);
will print
2÷3

Such escape sequences can be used in the middle of a string.

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

languages such as Polish, Serbo-Croatian, and Hungarian require a number


of characters not shown here; these can be found in the Latin Extended-A
characters (with codes ranging from \u0100 to \u017F). Any other char-
acters, in almost any of the world’s languages, can be found by visiting the
Unicode consortium’s web site (http://unicode.org/ ).
You may notice that there are gaps in the tables. For example, there
are no graphic characters with codes in the range \u0000 to \u0019. Many
of these gaps are used by the somewhat oxymoronically named non-graphic
characters. A number of these characters have special representations in
Java, obtained by using escape sequences consisting of a backslash followed
by a letter. In addition, some graphic characters sometimes need escape
sequences to avoid misinterpretations of their meanings. The complete list
of such characters is shown in the following table.
Character Meaning
\b Backspace
\f Form feed
\n Newline
\r Carriage return
\t Tab
\’ Single quote
\" Double quote
\\ Backslash
600 APPENDIX C. CHARACTERS AND INTEGERS

Unicode Basic Latin Characters

Char Code Dec Char Code Dec Char Code Dec


blank \u0020 32 @ \u0040 64 ‘ \u0060 96
! \u0021 33 A \u0041 65 a \u0061 97
” \u0022 34 B \u0042 66 b \u0062 98
# \u0023 35 C \u0043 67 c \u0063 99
$ \u0024 36 D \u0044 68 d \u0064 100
% \u0025 37 E \u0045 69 e \u0065 101
& \u0026 38 F \u0046 70 f \u0066 102
’ \u0027 39 G \u0047 71 g \u0067 103
( \u0028 40 H \u0048 72 h \u0068 104
) \u0029 41 I \u0049 73 i \u0069 105
∗ \u002A 42 J \u004A 74 j \u006A 106
+ \u002B 43 K \u004B 75 k \u006B 107
, \u002C 44 L \u004C 76 l \u006C 108
- \u002D 45 M \u004D 77 m \u006D 109
. \u002E 46 N \u004E 78 n \u006E 110
/ \u002F 47 O \u004F 79 o \u006F 111
0 \u0030 48 P \u0050 80 p \u0070 112
1 \u0031 49 Q \u0051 81 q \u0071 113
2 \u0032 50 R \u0052 82 r \u0072 114
3 \u0033 51 S \u0053 83 s \u0073 115
4 \u0034 52 T \u0054 84 t \u0074 116
5 \u0035 53 U \u0055 85 u \u0075 117
6 \u0036 54 V \u0056 86 v \u0076 118
7 \u0037 55 W \u0057 87 w \u0077 119
8 \u0038 56 X \u0058 88 x \u0078 120
9 \u0039 57 Y \u0059 89 y \u0079 121
: \u003A 58 Z \u005A 90 z \u007A 122
; \u003B 59 [ \u005B 91 { \u007B 123
< \u003C 60 \ \u005C 92 | \u007C 124
= \u003D 61 ] \u005D 93 } \u007D 125
> \u003E 62 ˆ \u005E 94 ˜ \u007E 126
? \u003F 63 \u005F 95 delete \u007F 127
601

Unicode Latin-1 Supplement Characters

Char Code Dec Char Code Dec Char Code Dec


\u00A0 160 À \u00C0 192 à \u00E0 224
¡ \u00A1 161 Á \u00C1 193 á \u00E1 225
/c \u00A2 162 Â \u00C2 194 â \u00E2 226
£ \u00A3 163 Ã \u00C3 195 ã \u00E3 227
¤ \u00A4 164 Ä \u00C4 196 ä \u00E4 228
=
Y \u00A5 165 Å \u00C5 197 å \u00E5 229
\u00A6 166 Æ \u00C6 198 æ \u00E6 230
§ \u00A7 167 Ç \u00C7 199 ç \u00E7 231
¨ \u00A8 168 È \u00C8 200 è \u00E8 232
c \u00A9 169 É \u00C9 201 é \u00E9 233
a
\u00AA 170 Ê \u00CA 202 ê \u00EA 234
 \u00AB 171 Ë \u00CB 203 ë \u00EB 235
¬ \u00AC 172 Ì \u00CC 204 ı̀ \u00EC 236
\u00AD 173 Í \u00CD 205 ı́ \u00ED 237
r \u00AE 174 Î \u00CE 206 ı̂ \u00EE 238

\u00AF 175 Ï \u00CF 207 ı̈ \u00EF 239

\u00B0 176 Ð \u00D0 208 ð \u00F0 240
± \u00B1 177 Ñ \u00D1 209 ñ \u00F1 241
2
\u00B2 178 Ò \u00D2 210 ò \u00F2 242
3
\u00B3 179 Ó \u00D3 211 ó \u00F3 243
´ \u00B4 180 Ô \u00D4 212 ô \u00F4 244
µ \u00B5 181 Õ \u00D5 213 õ \u00F5 245
¶ \u00B6 182 Ö \u00D6 214 ö \u00F6 246
• \u00B7 183 × \u00D7 215 ÷ \u00F7 247
¸ \u00B8 184 Ø \u00D8 216 ø \u00F8 248
1
\u00B9 185 Ù \u00D9 217 ù \u00F9 249
o
\u00BA 186 Ú \u00DA 218 ú \u00FA 250
 \u00BB 187 Û \u00DB 219 û \u00FB 251
1/4 \u00BC 188 Ü \u00DC 220 ü \u00FC 252
1/2 \u00BD 189 Ý \u00DD 221 ý \u00FD 253
3/4 \u00BE 190 Þ \u00DE 222 þ \u00FE 254
¿ \u00BF 191 ß \u00DF 223 ÿ \u00FF 255
Appendix D

HTML and Applets

An applet is a kind of Java program that can be embed-


ded in a document, sent across a network, and executed
under the control of a web browser. The documents
that carry applets are in files that contain descriptions
of their appearance using a language called HyperText
Markup Language (HTML). In this appendix, we first
look at some of the features of HTML and then show
how to create simple applets.
604 APPENDIX D. HTML AND APPLETS

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 resulting text would look something like this:


preceding stuff italicized stuff 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>

To display this in a browser, first save it in a file with extension .html


or .htm and then load the file into the browser. When displayed, the file
should produce a window more or less like the following (depending on the
606 APPENDIX D. HTML AND APPLETS

environment in which you are working).

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

to be displayed. Instead, we must use tags to indicate our wishes.

• The <br> tag indicates that we want a line break at a particular


point. The <br> tag stands alone; there is no corresponding </br>
tag. It is an example of an empty tag.

• 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.

HTML uses a device similar to Java’s use of escape sequences to print


a number of special characters. These include the characters < and >,
used to denote the beginning and end of a tag. To print such characters,
HTML uses an ampersand (&) followed by some code and terminated by a
semi-colon. Such a sequence is known as an entity in HTML. For example,
the entity &lt; is displayed as the character <. The technique is also used
to force the printing of extra blanks which are normally ignored in HTML.
The entity &nbsp; forces a blank. The next table shows some of the most
commonly used entities.

Character Entity
space &nbsp;
< &lt;
> &gt;
& &amp;

We can also use numeric entities to produce any of the characters


shown in Appendix C. A numeric entity has the form &#xxx; where xxx
is a number from 0 to 255, the decimal value of the character’s Unicode
representation. For example, writing &#177; would produce the character
± while the sequence gar&#231;on would be displayed as garçon.
Headings come in a variety of sizes, numbered from 1 to 6, with 1
being the largest and 6 the smallest. As usual, the exact size will be
browser dependent. The tags for a size n header are <hn> and </hn>.
608 APPENDIX D. HTML AND APPLETS

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

If we wish to centre any text, including headers, we can do so by using


the tags <center> and </center>. Any text surrounded by these tags will
automatically be centred in the window in which it is displayed.

Lists

HTML offers a number of structures for creating lists. The simplest of


these is an unordered list, a list consisting of a sequence of bulleted items.
The entire list is surrounded by <ul> and </ul>, while each item in the
list is preceded by the tag <li> (for l ist i tems).

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

would produce a display like the following.

If we want the items in a list to be numbered, we can do so by using


an ordered list. Here the entire list is surrounded by the tags <ol> and
</ol>. As with unordered lists, each item is preceded by <li>.

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

<dt> Good Things


<dd> chew sticks, bones, naps, long walks

<dt> Acceptable Things


<dd> short walks, kibble, mum grooming me

<dt> Bad Things


<dd> cats, squirrels, the vet, dad grooming me
</dl>
612 APPENDIX D. HTML AND APPLETS

will be displayed as

Images and Colours


In addition to displaying text, HTML files can also be used to display
images of various kinds. To display an image, we must first have a file that
contains the required image. We can then display it using an <img> tag.
These tags have a structure that is slightly different from any that we have
seen up to now. The next example illustrates the use of an <img> tag.

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.

Images can be encoded in many different file formats. Both GIF


(Graphics Interchange Format) and JPEG (Joint Photographic Experts
613

Group) formats are supported by most browsers (and in Java by Swing).


Files of these types should have identifiers that end with .gif (for GIF
files) and .jpeg or .jpg (for JPEG files).
To determine the location at which an image will be displayed, a
browser will, essentially, treat the image in the same way that it would
treat text. If the image tag appears between two words, the image will be
displayed there, with the base of the graphic aligned with the baseline of
the adjacent text. If we want an image to appear by itself on a line, we
can put line breaks just before and just after the image code.
If a browser does not have the ability to display graphic images or if
the file containing the image cannot be found, we can specify a piece of
text that should be displayed instead of the image by adding an attribute
with the name alt inside an img tag.

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.

Another way to heighten the interest of a display is to add colour.


Two simple features that we can employ are to set the colour of the text
or to set the colour of the background. In either case, we can set a colour
using a scheme similar to the RGB specification described in Section 13.3.
The primary difference between the two schemes is that, with HTML, the
intensity of each of the component colours is given as a hexadecimal value
(ranging from 00 to FF) rather than a decimal value (ranging from 0 to
255).1 The six digits of the colour code are preceded by the # symbol. The
table shows the codes of a few basic colours.
Code Colour
#000000 black
#FFFFFF white
#FF0000 red
#00FF00 green
#0000FF blue
1 See Appendix C for a discussion of hexadecimal numerals.
614 APPENDIX D. HTML AND APPLETS

A further complication in specifying colour is that not all environments


are capable of displaying all the colours that can be specified by the RGB
scheme. To be on the safe side, specify colours using only the following
values for each component: 00, 33, 66, 99, CC, and FF. The 216 possible
colours that can be specified in this way should display properly in almost
any environment.
The default colour for text is, of course, black. To change it to some-
thing else we can include a colour specification in the tag that opens the
body of a document. The default colour for the background is white or
light grey but this can also be changed. Both of these ideas are illustrated
in the next example.

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

• a file anywhere on the internet


615

• some other location in the current file


The simplest link is to another HTML file in the same directory as
the current HTML file. In this case, the pathToDocument is simply the file
name. If the file is in a subdirectory of the current document’s directory,
then this can be handled easily.

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.

To link to a file on the internet, we use a similar form of anchor tag.


Now, however, the value assigned to href consists of a Uniform Resource
Locator (URL). URLs can take many forms but the most familiar one
begins with http (hypert ext t ransfer protocol) followed by ://, then the
internet address of a remote computer, and finally the location of a file on
that computer.
To enable us to jump from one location in a file to another location
in the same file, we have to do two things: mark the point to which we
wish to jump and mark the point from which we wish to jump. The next
example illustrates these processes.

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

The solutions to the test are now available . . .


where clicking on the phrase solutions to the test will produce a jump to
the heading for the test solutions.
To do this, we first mark the destination point, using a new form of an
anchor tag that assigns a hidden label to that location.
<h2> <a name = "solns"> Test Solutions </a> </h2>
Then, we create an anchor at the source text.
The <a href = "#solns"> solutions to the test </a> are ...
When the file is displayed, the appearance of the heading will be unaffected
by the addition of the hidden anchor tag but a click on solutions to the test
will produce a jump to the heading.

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.

• code — the .class file containing the applet byte code

• width — the width, in pixels, that the applet will occupy on the
screen

• height — the height, in pixels, of the applet

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>

Applets can be created in a variety of ways in Java. As usual, we will


not be exploring every possibility. Instead, we will limit ourselves to a style
that is very close to that developed in Chapter 13. Our applet classes will
be extensions of the class javax.swing.JApplet. The diagram shows a
partial hierarchy diagram for the JApplet class.

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.

• public void start ()


The start method is called after the init method completes execu-
tion and to restart any applet that is returning to visibility after a
stop.

• public void destroy ()


This method is called when the user of the browser quits the browsing
session. It is used to perform any cleanup that may be required at
that time.

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.*;

public class AppletGreet extends JApplet


{
public void init ()
{
JPanel pane = (JPanel) getContentPane();
pane.add(new BigGreet());
}

class BigGreet extends JComponent


{
619

public void paint (Graphics g)


{
Font largeSerifFont = new Font("Serif",Font.PLAIN,40);
g.setFont(largeSerifFont);
g.drawString("Hello, world",100,50);
}
}
}
If you compare this program with the one in Section 13.1, you will notice
that this is similar but somewhat simpler. Let us look at the features that
distinguish the applet from the application.
1. import ...
The number of import statements has been reduced. Since control
of such things as window closing events are now in the control of the
browser, there is no need to import the class WindowEvent from the
package java.awt.event.
2. public class AppletGreet extends JApplet
In writing an application, we created a JFrame object in our main
method to act as a container for the pane that contained the graphical
display. Since a JApplet is a descendant of Container, a JApplet
object itself can act as a container for a pane.
3. public void init ()
Since the browser is in control of the applet, the init method that
is executed when the applet is first loaded has far less to do than the
main method of the application. Our init method simply creates
a pane and then adds a newly constructed BigGreet object to that
pane.
4. class BigGreet extends JComponent
Again this is simplified because we no longer need a constructor con-
taining the repaint method. With applets, the paint method is
called automatically whenever it is needed.

To display the applet, we need to create an HTML document that


contains tags for the applet. The following file will serve the purpose for
the applet of Example 13.
620 APPENDIX D. HTML AND APPLETS

<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.

Rather than use a browser to display an applet, we can also use a


program supplied by Sun called appletviewer which displays an applet
without any other part of the HTML file that contains the applet tags.
For our example, use of appletviewer would produce a display like the
621

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.*;

public class VariedAppletGreet extends JApplet


{
private static int fontSize = 30;

public void init ()


{
JPanel pane = (JPanel) getContentPane();
pane.add(new Greeting());
}
622 APPENDIX D. HTML AND APPLETS

public void start ()


{
fontSize = (int)(40*Math.random() + 10);
}

class Greeting extends JComponent


{
public void paint (Graphics g)
{
Font serifFont= new Font("Serif",Font.PLAIN,fontSize);
g.setFont(serifFont);
g.drawString("Hello, world",100,50);
}
}
}
Appendix E

Exception Handling

For any program, it is almost inevitable that sooner or


later something will go wrong. This may happen for
a variety of reasons including programmer error, bad
input, or physical problems with the system. Java’s ex-
ception handling facilities provide a flexible mechanism
for detecting and recovering from program failure.
624 APPENDIX E. 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

If an exception is thrown in a method with a throws clause for that


type of exception, execution of that method is halted immediately and
control is passed up to the method that called the method that threw the
exception. If that method also has a throws clause in its header, then the
process continues. If the main method throws an exception, then control
is passed to the JVM and the entire program is halted.
As an alternative to simply throwing an exception up to a calling
method, we can, at any point, actually try to deal with it. To do so, we
use a new Java structure: the try/catch/finally statement. The first
part of such a statement consists of the word try followed by a block of
code that contains one or more statements that might throw an exception.
The try block must be followed by one or more catch clauses and/or a
finally clause. The try block, each of the catch blocks, and the finally
block must be contained in brace brackets, even if they only contain one
statement.
The purpose of each catch clause is to contain a block of code that
should be executed if a particular type of exception occurs. Each catch
clause begins with the word catch followed by a set of parentheses contain-
ing a variable declaration. The type of the variable being declared must be
Throwable or one of its subclasses.

Example 2
A catch clause that could be used to catch an EOFException might have
the form

catch (EOFException e)
{
...
}

where ... indicates the statements that would be executed if an EOFEx-


ception were to be thrown in the preceding try block.
626 APPENDIX E. EXCEPTION HANDLING

For a catch clause is to be executed, the type of its exception must


match the type of the thrown exception. To be considered a match, the
type of the exception in the catch clause must either be identical to that
of the thrown exception or have the type of one of its superclasses.

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

The finally clause, if it is present, will be executed after normal


termination of the try block. If an exception is thrown and it is caught,
the finally block will be executed after execution of the catch block. If
the exception is not caught, the finally block will be executed before the
exception is thrown to a calling method. The finally clause is useful for
tasks, such as closing files, that should always be done before a program
terminates.
A catch clause can contain code that will allow a program to recover
from an exception. Illustrations of this can be found in the class In, shown
in Appendix A. There, if an exception occurs in trying to read from the
standard input, default values are returned (zero for numerical values and
"" for strings). A more complex possiblility is shown in the next example.

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;
}

If we wanted to increment the value of n after inserting the new element in


the array, we could do so in a finally clause, to be placed after the catch
clause.

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.)

We can throw our own exceptions. One way to do so is to create a new


object of type Exception (or one of its subclasses) that we have customized
for our particular situation.

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.

Any time that we construct an exception with a string argument, as


we did in Example 7, then the toString method of the exception will
return the name of the class of the exception object, followed by a colon
and a space, followed by the string that we used in the constructor. If
the exception is thrown, the toString method is invoked to print this
information.
Appendix F

The Javadoc Program

To assist a programmer in documenting programs, Sun


has included the Javadoc tool with their Java software
development kits. This tool produces documentation in
a standard form directly from comments written in a
special form in the program. This appendix gives an
introduction to this tool.
632 APPENDIX F. THE JAVADOC PROGRAM

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.

Doc comments can be used in a program to document packages, classes,


interfaces, methods, and fields. Each doc comment should be placed imme-
diately before the item that it is describing. Once a file has been created
using doc comments, it can then be processed by the Javadoc program
to produce an HTML file that documents the features in a style identi-
cal to that used by Sun for the Java API. In order to be able to produce
such a file, we must follow an appropriate structure when creating these
comments. We show here the basic features of this structure.
633

The Description Section


The first sentence of each doc comment should be a short but complete
description of the item. In writing this description, there are a number of
points that should be observed.

• Although the description is referred to as a sentence, it need not be


an actual English sentence; phrases are quite acceptable. Method
descriptions should begin with a verb phrase (such as “computes
final tax payable”). Field descriptions (or other descriptions of
things rather than actions) should simply state what the item is (such
as “radius of a circle”).

• Whether or not the description is actually a sentence, it should be


terminated by a period.

• Since a doc comment can be used to create an HTML file, it is valid


and appropriate to use HTML tags in the comment. For example,
Java keywords and identifiers should be surrounded by <tt>...</tt>
or <code>...</code> tags.

• Even if the identifier of the element being commented is very descrip-


tive, the comment should not simply repeat the identifier in sentence
form; it should add something useful to the identifier.

• In documenting overloaded methods, be sure that the descriptions


distinguish these methods from one another.

The Tag Section


If present, the tag section of a doc comment consists of a sequence
of tags, each of which is followed by a comment for that tag. Comments
associated with one tag can take up more than one line. Extra blanks are
ignored.
The tags all have the form @<tag>. The form that each associated
comment takes varies as indicated in the following list. The tags should be
given in the order shown. The list is not complete; for a full discussion of
doc comments, see Sun’s web site. At the time this was written, the page
containing full instructions on writing doc comments was located at
http://java.sun.com/j2se/javadoc/writingdoccomments/index.html
634 APPENDIX F. THE JAVADOC PROGRAM

• @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

The use of <label> is optional. If it is present, then whatever is in


<label> will be displayed at the point at which the tag appears and
a hyperlink will be provided to the documentation of <package>-
.<class>#<member>.
Notice that in writing these tags, brace brackets must be used to
separate the tag’s contents from the surrounding text. Any number
of @link tags can appear in a doc comment and they can be used
either in the description section or the tag section.

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.

Running the Javadoc Program


Once we have created a file using doc comments, we can then cre-
ate an HTML file describing its structure. To run Javadoc on the file
Sample.java, we can use the command
javadoc Sample.java
This will create a number of HTML files that can be displayed in a browser.
One of them, called Sample.html, will contain information on where the
class Sample fits into Java’s class hierarchy, along with a summary of its
fields, constructors, and methods as well as detailed notes on its fields, con-
structors, and methods. The format is exactly the same as that in Sun’s
API documentation. To examine this format, see Sun’s web site. At the
time this was written, the index page for the API was located at
http://java.sun.com/j2se/1.4/docs/api/index.html
The program can be run with a number of options. Each of them is
specified by a keyword with a preceding minus sign. If any of these options
638 APPENDIX F. THE JAVADOC PROGRAM

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 ^.

Boolean And (&)


Recall from Chapter 3 that, to evaluate an expression of the form p && q,
where p and q are boolean-valued expressions, Java first evaluates p, the
left hand operand. If the value of p is false, then the value of the entire
expression must also be false so Java does not examine q. This process is
usually called lazy or short circuit or conditional evaluation. If, for some
reason, we always want both operands to be evaluated, we can use the
operator & rather than &&. Other than this, the effect of using && or &
is the same. Thus, for &, the result is true if both p and q are true;
otherwise, the result is false. To distinguish between the two operators,
&& is sometimes referred to as the conditional boolean and operator.

Boolean Inclusive Or (|)


We also saw in Chapter 3 that conditional evaluation occurs with expres-
sions of the form p || q, where p and q are boolean-valued expressions.
Here, if the value of p is true, then the value of the entire expression must
also be true so Java does not examine q. If we always want both operands
to be evaluated, we can use the operator | rather than ||. Other than
this, the effect of using || or | is the same. Thus, for |, the result is true
if p alone is true, or q alone is true, or both p and q are true; the result
is false only if both operands are false. Again, to distinguish between
the two operators, || is sometimes referred to as the conditional boolean
or operator.

Boolean Exclusive Or (^)


Sometimes we want to test whether or not exactly one of two expressions
is true. For this situation, Java has another operator: ^, the exclusive or
operator. If p and q are boolean-valued expressions, the value of p ^ q is
true if p alone is true or if q alone is true; the result is false if both
operands are false or both operands are true. A simpler way of thinking
about the ^ operator is to note that p ^ q is true if the values of p and q
are different; otherwise, the value is false.
641

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

(a) If we now write


int c = a&b;
then the resulting bit representation of c will be
00001100 & 00001010
⇒ 00000000 00000000 00000000 00001000
The integer value of c will be 8 because 10002 = 810 .

(b) The statement


int d = a|b;
will assign to d the value 14 because
00001100 | 00001010
⇒ 00000000 00000000 00000000 00001110
and 11102 = 1410 .
642 APPENDIX G. JAVA OPERATORS

(c) The statement


int e = a^b;
will assign to e the value 6 because
00001100 ^ 00001010
⇒ 00000000 00000000 00000000 00000110
and 1102 = 610 .

(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.

Left Shift (<<)


The operator << shifts the bits of its operand to the left. Bits shifted out
of the left end are lost while bits on the right are set to zero.
Just as adding a zero to the right end of a base 10 numeral multiplies
the value by 10, adding a zero to the right end of a base 2 numeral multiplies
its value by 2. Thus, shifting the bits left by n places (adding n zeros to
the right end) has the effect of multiplying the value by 2n .

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 .

Signed Right Shift (>>)


There are two right shift operators in Java, >> and >>>. They both operate
in a manner that is analogous to the left shift operator. The difference
between the two is seen in their action on the left end of the operand. The
operator >> replaces the left end bits with whatever bit was originally in
the leftmost position of the operand.

Unsigned Right Shift (>>>)


The operator >>> functions exactly like the >> operator except that it
always replaces the left end bits with zeros.

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

The next example uses a variety of operations to analyze a bit pattern.


It also uses a mask, a value containing a known pattern of bits, to extract
bit patterns from an unknown integer value.
Notice in the example the use of the operator >>>=. The bitwise op-
erators &, |, and ^ as well as the shift operators <<, >>, and >>> can all be
combined with the assignment operator =. The resulting operators act in
a manner similar to that defined for += on Page 65.

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.

Conditional expressions are often a convenient alternative to if state-


ments that assign a particular value to a variable depending on the result
of some test.
645

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;

Conditional expressons can be nested inside other conditional expres-


sions. In the absence of parentheses, such expressions are evaluated from
right to left.

Example 7
The signum function has the following definition:

−1 if x < 0

signum(x) = 0 if x = 0


1 if x > 0

To assign to the variable signum the value of signum(x), we can write


signum = x < 0 ? -1 : x == 0 ? 0 : 1;

The expression on the right side of the assignment would be interpreted by


Java as
x < 0 ? -1 : (x == 0 ? 0 : 1)

Precedence and Associativity


In expressions that have more than one operator, it is often important to
know which operations are done first. The following table illustrates the
rules that govern the order of evaluation of Java’s expressions.
646 APPENDIX G. JAVA OPERATORS

A Summary of Java’s Operators

Operator Operation Associativity


. member selection
[] array index selection left to right
() method call
++ -- increment, decrement
+ - unary plus, minus
! boolean not right to left
~ bitwise complement
(<type>) cast to <type>
* / % multiplication, division, remainder left to right
+ - addition/concatenation, subtraction left to right
<< >> >>> bitwise shifts left to right
< <= > >= relational ordering
left to right
instanceof test of type of an object
== != relational equality, inequality left to right
& boolean or bitwise and left to right
^ boolean or bitwise exclusive or left to right
| boolean or bitwise inclusive or left to right
&& conditional boolean and left to right
|| conditional boolean or left to right
? : conditional right to left
= += -= *= /= %=
&= |= ^= assignments right to left
<<= >>= >>>=

Operations in the table are listed from highest precedence to lowest.


If an expression contains different operators, those with higher precedence
are evaluated first. Operators with equal precedence appear between the
same set of lines.

Example 8

(a) In the expression 1 + 2 ∗ 3, the operation ∗ has higher precedence


than + so the multiplication is performed before the addition.
1 + 2 * 3
⇒ 1 + 6
⇒ 7
647

(b) Of course, if we want to override the precedence rules, we can do so


using parentheses.
(1 + 2) * 3
⇒ 3 * 3
⇒ 9

If an expression contains either different operators with the same prece-


dence or repetitions of the same operator, then the order of evaluation is
determined by the operator associativity. If the associativity is from left to
right, the leftmost operation is performed first; if the associativity is from
right to left, the rightmost operation is performed first.

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

This appendix contains answers to all questions in the


text that do not require programming. A manual,
available only to instructors, contains solutions to both
non-programming questions and programming questions
other than those in the Projects sections of the Review
Exercises.
650 APPENDIX H. ANSWERS TO EXERCISES

1.1 (d) 0.02677


1. (a) Hardware and software (e) −0.054
(b) CPU, memory, and I/O devices (f) −3.0
(c) Control and ALU 2. Answers may vary
2. (a) CPU (ALU) 3. (a) double
(b) I/O device (b) long
(c) CPU (ALU) (c) int
(d) Memory (d) boolean
(e) CPU (control) (e) double
3. In files (f) double
4. Difficult to write and understand, (g) float
not portable between systems (h) char
5. Source program written by a
programmer; object program is 1.6
machine code version 1. Class identifiers start with upper
6. Object code produced by Java case letters (by convention)
compiler cannot run on an actual 2. (a) Valid
computer; it must be run by JVM (b) A reserved word
(c) Contains a blank
1.2 (d) Valid
1. If at first you don’t succeed, (e) Contains the character @
failure may be your style (f) Does not follow our conventions
(g) Contains hyphens
1.4 (h) Valid
1. 16 (i) Valid
2. (a) 127 (j) Does not follow our conventions
(b) 9 223 372 036 854 775 807 3. (a) int quantity, size;
(c) 32 767 double price;
(d) 2 147 483 647 (b) double income, rate, discount;
3. (a) int char taxCode;
(b) long 4. Answers may vary
(c) byte
(d) short 1.7
4. (a) Valid 1. (a) double to int requires a cast
(b) Invalid - decimal point (b) Valid
(c) Valid (c) double to float requires a cast
(d) Invalid - space (d) char value should be written ’+’
5. 4 294 967 295 (e) Valid
(f) Valid
1.5 2. (a), (c), (d), (e) require a cast
1. (a) 29.4 4. (a) Cast is wrong and not required;
(b) 0.4 change second statement to
(c) −0.002 int i = s;
651

(b) Cast is required; while j is 35.


change second statement to (b) We are not false yet
short s = (short)i; (c) x -> 0.012
y -> 27.374
1.8
1. (a) 1.10
1. Make programs easier to understand,
a b modify; help avoid errors
1
1.11
- "2" 1. (a) Syntax – “were” should be “was”
Logic – younger born before?
(b) Logic – what comma?
(b)
(c) Syntax – “parts” should be
a b “part”; Syntax – extra period
3. Line 2 – comment left open
Line 4 – "String", not "string"
Line 6 – assigning 34.0 to int
- "first" - "second" Line 7 – use ", not ’
use +, not ,
(c) 4. Answers may vary

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

(c) byte (d)


(d) long
5. (a) 0.000877 s t
(b) 299.04
(c) −44300
(d) −0.0023
- "this"  "that"
6. (a) Illegal – starts with digit
(b) Legal but does not follow our
conventions - first 2.1
character upper case 1. (a) 5
(c) Legal but does not follow our (b) Invalid – must have *
conventions - currency (c) 198.0
symbols restricted to (d) 7
system programs (e) 7.5
(d) Legal (f) 0L
(e) Illegal – contains a blank (g) Invalid – integer division by 0
(f) Illegal – a reserved word (h) 2.4f
7. (b), (e), and (f) (i) 35
8. (a) x: 0.0093y: -0.003 (j) Invalid – cannot use [ ]
(b) first is 5 (k) 0.0
second is -77 (l) 0.375
10. (a) 2. (a) 2
(b) 3
s t (c) 8
(d) 0
(e) 0
(f) 2.7
- "this" - "that" (g) 1.0
(h) 2.9f
(b) (i) NaN
(j) -Infinity
s t (k) −1
(l) −0.5
3. (a) a * b / (c + d)
(b) a * b - c % d
- "that" - "this"
(c) (a - b) % c / (d * (e + f))
(d) a * b - c / d - e / (f / g)
(c) 4. (a) x/2 + 4*(y - 3)
(b) x*x*x + y*y*y
s t (c) 2*(x + 1)/(y - 2)
(d) (x/y + z)/(x + y/z)
5. Answers may vary
- "that"  "this" 2.2
653

1. (a) 0.6 (d) 1.5


(b) 2 2. (a) 4 11
(c) 1.0 (b) 28 7
(d) 2.0 (c) −3 6
(e) 2 (d) 24 6
(f) 1.3 (e) 31
(g) 1 (f) 51
(h) Infinity (g) 40 8
(i) 6.1 (h) 37
(j) 1.0 3. (a) 111
2. (a) 9 (b) 331
(b) 5 + 22 (c) 931
(c) 12 (d) 032
(d) i * j 2 (e) 962
3. (a) i 4 (f) 012
(b) 11 4. (a) Cannot assign a value to i+3
(c) i + j -> 47 (b) Cannot assign a value to j-2
(d) 11 <- i + j
(e) i * j -> 28 2.5
1. (a) 4
2.3 (b) 5
1. (a) 444 (c) −3
(b) 425 (d) −5
(c) 5 16 5 2. (a) ’y’
(d) 411 (b) ’5’
(e) 12 1 6 (c) 4
(f) 430 (d) 1
2. (a) 84 3. (a) Assigns a the value ’d’
(b) 64 (b) Invalid – b = (char)(b-2);
(c) 23 5 Assigns b the value ’2’
(d) 22 5 (c) Assigns c the value −5
(e) 0 13 (d) Invalid – dd = (char)d++;
(f) 09 Assigns dd the value ’7’
3. (a) y = ++x - z++; (e) Invalid – ee = (char)e;
(b) z = ++x*y - 1; Assigns ee the value ’t’
4. (a) m=n-p; n++; p--; (f) Assigns ff the value 2
(b) ++n; p=n-m; m--;
5. −1 0 2.6
1. (a) −2 int
2.4 (b) 0.12 double
1. (a) 6.0 (c) 0.08 double
(b) 6.0 (d) 2.7 double
(c) 0.0 (e) 3 long
654 APPENDIX H. ANSWERS TO EXERCISES

(f) 0.095 double (b) 0.5


(g) 5.0 double (c) 1.0
(h) −5.0 double (d) 0.0
(i) 7.0 double (e) 0.5
(j) −2.0 double (f) 1.0
(k) 2.0 double 9. (a) Math.sin(Math.PI/4)
(l) 0.0625 double (b) Math.cos(2*Math.PI/3)
(m) 2 long (c) Math.tan(Math.PI/15*2)
(n) 1.1 double (d) 1/Math.cos(Math.PI/10)
2. (a) math.sqrt(a*a-b*b) (e) 1/Math.sin(Math.PI*8/9)
(b) Math.PI*(Math.pow(x,6) (f) 1/Math.tan(Math.PI/12)
-Math.pow(y,6)) (g) Math.atan(2)
(c) 4.0/3*Math.PI*r*r*r (h) Math.asin(0.5)
(d) Math.abs(Math.pow(z,4)-1) (i) Math.atan(1/Math.sqrt(3))
(e) Math.log(1+x) (j) Math.acos(1/1.5)
(f) x*x*Math.exp(x) 10. Math.log(x)/Math.log(10)
3. The value is 1. Math.random 11. (a) change PRECISION to 0.1
returns a random value in the (b) Change PRECISION to 1000,
interval 0 ≤ x < 1. types of PRECISION and
When cast as an int, the result is roundedValue to long
zero and 6 × 0 + 1 = 1.
4. (a) result = (int)
(10*Math.random())+1; 2.7
(b) result = (int) 1. (a) 2
(52*Math.random())+1; (b) 3
(c) result = 5*(int) (c) 3.6
(20*Math.random())+5; (d) 2
(d) result = (int) (e) 4
(11*Math.random())-5; 2. (a) 1 – 0.999 999 999 999 985 8
(e) result = 10*(int) roundoff error
(21*Math.random())+100; (b) 7.05 × 10308 – Infinity
(f) result = b*(int) result is too large for a double
((k+1)*Math.random())+a; (c) 18.0 – 0.0
5. randChoice= roundoff of first sum loses 18
(char)(’A’+5*Math.random()); entirely;
6. randVal=0.25* then 1E18 - 1E18 gives zero
((int)(13*Math.random())+4); (d) 18.0 – 18.0
7. (a) dodecaRoll= subtracting large values first
(int)(12*Math.random())+1; gives 0.0;
(b) doubleRoll= then 0.0 + 18 = 18.0
(int)(6*Math.random())+1 (e) 1.0 – 0.0
+(int)(6*Math.random())+1; first product underflows to zero;
8. (a) 0.5 then zero × any number is zero
(f) 1.0 – Infinity
655

first product overflows to (Math.tan(x),2));


Infinity; then Infinity × any (c) t=Math.log(Math.abs(
number is Infinity 1 - Math.pow(x,4)));
(d) y=1/Math.sqrt(2*Math.PI)
* Math.exp(-x*x);
2.8 8. (a) 4 and 4
1. (a) 5 (b) temp=x;x=y;y=temp;
(b) 20.3
(c) 0.0
(d) 0.0 3.1
(e) 6.0 1. (a) true
(f) 8.3 (b) illegal – space between < =
(g) −10 (c) false
(h) 0 (d) false
(i) 1.5 (e) Illegal – should be <=
(j) 0.7 (f) Illegal – should be ==
2. (a) Unbalanced parentheses (g) Illegal – incomparable types
(b) Correct (h) true
(c) Missing asterisks 2. perhaps: true (4 < 5)
(d) Correct maybe: false (−1 6= 1)
(e) Missing parentheses 3. (a) false
(f) Missing parentheses (b) false
3. (a) 481 (c) false
(b) 432 (d) true
(c) 2 5 −1 (e) true
(d) 423 (f) false
(e) 631 (g) true
(f) 623 (h) false
4. (a) ’h’ 4. (a) true
(b) −7 (b) true
5. (a) 4 (c) false
(b) −4.0 (d) false
(c) 0.0012 (e) true
(d) 16.0 (f) true
6. (a) Math.sqrt(Math.PI 5. (a) Math.abs(x-y)<Math.abs(1e-6*x)
* Math.abs(a*a-b*b)) (b) to compare relative sizes
(b) Math.pow(Math.pow(x,6)
+ Math.pow(y,6),0.25) 3.2
(c) (-b+Math.sqrt(b*b 1. (a) "cat" (’c’ < ’d’)
- 4*a*c))/2*a (b) "Cathy" (’C’ < ’c’)
(d) 1.0/(1.0/a+1.0/b) (c) " X" (’ ’ < ’X’)
7. (a) y=1/(Math.pow(Math.sin(x),2) (d) "CAR"(’C’ < ’c’)
- Math.pow(Math.cos(x),2)); (e) "X X" (’ ’ < ’X’)
(b) s=Math.sqrt(1+Math.pow (f) "XY" (strings same until "XY"
656 APPENDIX H. ANSWERS TO EXERCISES

ends) (c) p && q


(g) "375" (’3’ < ’8’) (d) x == 0 || y == 0
(h) "" (strings same until "" ends) 5. (a) p
2. (a) false (b) !p || q
(b) false (c) !p
(c) true (d) true
(d) false (e) false
3. (a) negative (f) p
(b) positive (g) p
(c) positive (h) !p
(d) positive 6. p != q
(e) negative
(f) negative 3.5
(g) zero 2. (a) Nothing
(h) negative (b) Reject
4. (a) s1.compareTo(s2) == 0
(b) s1.compareTo(s2) < 0 3.6
(c) s1.compareTo(s2) >= 0 2. (a) 6
(d) s1.compareTo(s2) <= 0 (b) 6
16
3.3 4
1. (b) Eligible to vote
3.7
3.4 1. (b) i. true
1. (a) false (b) ii. false
(b) true (b) iii. true
(c) false (b) iv. true
(d) true 3. (a) Prints for any value of x;
(e) true second println is not in the if
(f) true 4. (a) a > 0 and b < 0
(g) true (b) a > 0 and b ≥ 0
(h) false (c) a ≤ 0
(i) true
(j) false 3.8
2. (a) a > b 1. (a) true
(b) x == 0 (b) false
(c) x < 5 (c) true
(d) true (d) false
(e) a >= b 2. (a) false
(f) false (b) false
3. i >= 0 && i <= 5 (c) illegal – should be ==
4. (a) !p || q (d) true
(b) p || q (e) illegal – cannot have a < b < c
657

(f) false (d) 2


5. In every case, it prints 4
Preferred flavour? 16
Preferred style?
The client preferred
followed by:
(a) vinegar, crinkled chips.
4.4
1. (a) 1 12
(b) bar-b-que, regular chips.
2 10
(c) vinegar chips.
3 8
(d) bar-b-que, crinkled chips.
4 6
(e) other chips.
(b) 1
(f) other chips.
1
9. (a) Notes whether or not
3
a, b, c are in order
(c) 1
2
4.1 4
1. 0 8
2. No maximum
3. 10 0
9 2
8 4
4.5
1. (a) Print the numbers from 1 to 9
7 6
along with their square roots
(b) Print the cubes of the numbers
4.2 from 4 down to 0
1. If body must be executed at least 2. (a) Print values of y = |x/5 − 2|
once for x = −10, −8, . . . , 8
2. How many items to add? (b) Determine product of numbers
Give a positive value. supplied by user
Give a positive value. 3. (a) Determine which value supplied
by user has maximum value
(b) Repeatedly ask for Y/N until
4.3 user supplies one of these
1. (a) *******
4. (a) for – a counted loop
(b) 5 seconds
(b) do – conditional loop
4 seconds
must be done at least once
3 seconds
(c) while – conditional loop
2 seconds
may execute zero times
1 seconds
(d) while – conditional loop
(c) Give me a P
may execute zero times
Give me a Q
(e) for – counted loop
Give me a R
Give me a S
658 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

(d) Invalid – cannot pass 0.5 to 5: a b c


either long or int 6: a b c f
(e) Valid – A – exact match 7: a b c g
(f) Valid – B – less widening

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

(b) (f) invalid, unfixable

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

(c) 6.0 bloop


6.0 Gus is alive
(d) m in A would now be Bubbles is dead
overridden by m in B 10. (a) 1
(b) 1
7.7 (c) 2
1. (d) 3
(e) 3
Vehicle (f) 4
(g) 1
6 (h) 3
Bicycle Car Truck
6 8.1
1. (a) marks
Convertible Hatchback (b) marks[0]
(c) 0
(d) 20
2. This is a call to a constructor of the
(e) 0 . . . 19
superclass (with argument value)
2. (a) 640
3. private: only visible in that class
(b) 6400
protected: also visible in subclasses
(c) 1600
4. Definition will be in a subclass
(d) 1000
5.overriding: one method hides another
5. (a) 4.0
in a superclass
(b) 10.8
overloading: different methods with
(c) 0...8
the same identifier
(d) mass[3]
6. (a) valid
(e) double
(b) invalid – compilation
(f) 9
(c) invalid – execution
(d) invalid – compilation
7. (a) title: Book 8.2
timePeriod: History 1. 4
author: Book 0
8. A 3
A
B 8.3
A 1. A: a
B B: a[0]
9. New pet: Gus - a cat C: a[2]
meeow D: a[0][0]
purr E: a[1][2]
Gus is alive F: a[2][3]
New pet: Bubbles - a fish 2. (a) 1000
bloop (b) 900
664 APPENDIX H. ANSWERS TO EXERCISES

(c) 2400 (b) Valid: second statement causes


(d) 1000 "old" to be discarded
3. (a) 427
391 9.2
(b) 43 1. (a) ’n’
29 (b) "hui"
71 (c) 11
(c) 391 (d) 2
427 (e) ’A’
(f) "man"
8.6 (g) "manda Chui"
2. Array needed only for the median (h) −1
3. (a) Missing [] after first double (i) ’d’
(b) No length specification (j) "d"
(c) Missing new 2. (a) "8"
(d) First dimension specification (b) "6"
missing (c) "E"
(e) Should not have new here (d) "true"
4. (a) It sums the entire array 3. (a) 0
(b) ’A’
8.7 (c) 5
1. No, it creates a variable that can (d) "I LAURIE"
refer to such an array (e) 5
2. (a) 0 (f) 6
(b) false
(c) 2 9.5
(d) null 1. (a) A compile-time error:
3. (a) 80 s has no value
(b) 30 (b) An execution-time error:
(c) 800 t refers to no string
(d) 2000 (c) 0: null string has zero length
(d) 1: a blank is a single character
9.1 2. (a) null: array references are
1. An error message automatically initialized to this
2. s is |null| (b) 10: array is of this size
t is || (c) Compile-time error: length is
u is | | a field of an array
3. (a) s is: Simple (d) Execution-time error: names[0]
t is: Sample is null so it has no length
(b) s is: Second 3. (a) $2
t is: First (b) 86
4. (a) We cannot alter the value of a (c) 38
string (d) $2
665

(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

(f) 2, 3.16, 3.67, 3.88, 3.95 4. (a) No base case


2. (a) t1 = 10, tn = tn−1 + 3 if n > 1 (b) Calls increase parameter
(b) t1 = 21 , tn = tn−1 + 1 if n > 1 5. (c) Non-recursive form should be
(c) t1 = 5, tn = 3tn−1 if n > 1 more efficient – it avoids
(d) t1 = −1, tn = tn−1 − 2 if n > 1 recursive method calls
(e) t1 = 256, tn = tn−1 ÷ 4 if n > 1 6. (a) i) 2
(f) t1 = 1.1, tn = tn−1 + 0.1 if n > 1 ii) 0
3. (a) 6 iii) −3
(b) 12 iv) −3
(c) 1 (b) f (x, y) calculates x − y
(d) 103 7. (a) a(1, 1) = 3
4. (a) 1 m, 0.5 m, 0.25 m, 0.125 m a(2, 1) = 5
(b) jn = 12 jn−1 , n > 1 (c) It fails; too many recursive calls
(c) 0.03125 m
(d) jn = 2−n
5. (a) 7 11.4
(b) 44 % 6 1. (a) 1 1
(c) Use remainders after division 2 3
not differences after subtraction 3 7
4 15
5 31
11.3 (b) i) 63
1. (a) gcd(20,28) ii) 127
calls gcd 28,20) iii) 1023
calls gcd(20,8) iv) 2n − 1
calls gcd(8,4) (c) 585 billion years
calls gcd (4,0) 2. output: 1-->2, 1-->3, 2-->3,
returns 4 1-->2, 3-->1, 3-->2, 1-->2,
(b) gcd(991,129) 1-->3, 2-->3, 2-->1, 3-->1,
calls gcd(129,88) 2-->3, 1-->2, 1-->3, 2-->3
calls gcd(88,41) 5. (a) convert(10,2) calls
calls gcd(41,6) convert(5,2)
calls gcd(6,5) which calls convert(2,2)
calls gcd(5,1) which calls convert(1,2)
calls gcd(1,0) to print 1 and return
returns 1 to print 0 and return
2. term(3) = 2 + 0.5*term(2) to print 1 and return
term(2) = 2 + 0.5*term(1) to print 0 and return
term(1) = 2 + 0.5*term(0) (b) convert(22,3) calls
term(0) returns 1 convert(7,3)
term(1) returns 2 + 0.5(1) = 2.5 which calls convert(2,3)
term(2) returns 2 + 0.5(2.5) = 3.25 to print 2 and return
term(3) returns 2+0.5(3.25) = 3.625 to print 1 and return
3. (a) It calls itself with n = −1, −2, . . . to print 1 and return
668 APPENDIX H. ANSWERS TO EXERCISES

(c) It prints value as a W . . . W . W + + - W


base base numeral W . W . W . W + W W W
(d) It would print value % base W . W . . . + + - - W
6. (a) Answers may vary W . W W W W + W W - W
7. (a) If n < 1, print nothing; W . W . . . + + W - W
else print an (n − 1)-triangle W W W W W W W + W W W
then print a row of n asterisks 3. W W W W W W W W W W W
8. It will print: W - - - W - W + + . W
12345 W - W - W - W + W W W
1234 W - W - - - + + . . W
123 W - W W W W + W W . W
12 W - W - - - + + W . W
1 W W W W W W W + W W W
9. **** 7. (a) A blob contains no asterisks
*** (b) To ERASE BLOB from current cell
** if cell contains *
* erase * in cell
for each adjacent cell
11.5 ERASE BLOB starting there
2. (a) To REVERSE a string
if length > 1 11.7
if length is even 1. Boldface indicates L/R markers
concatenate 12 7 20 6 21 18 2 14 15 5
REVERSE of 2nd half + 5 7 20 6 21 18 2 14 15 5
REVERSE of 1st half 5 7 20 6 21 18 2 14 15 20
else 5 7 2 6 21 18 2 14 15 20
concatenate 5 7 2 6 21 18 21 14 15 20
REVERSE of 2nd half + 5 7 2 6 18 18 21 14 15 20
middle character + 5 7 2 6 12 18 21 14 15 20
REVERSE of 1st half 2. (a) 31
3. cat (b) 129
cta 3. (a) 61
act (b) 496
atc 4. (a) First search is far more efficient
tca (b) Answers may vary
tac
11.8
11.6 1. (a) 2t s
1. (1, 8), (1, 9), (1, 8), (1, 7), (2, 7), (b) 8t s
(3, 7), (3, 8), (3, 9), (4, 9), (5, 9), (c) 2n t s
(4, 9), (3, 9), (3, 8), (3, 7), (3, 6), (d) 22n t s
(4, 6), (5, 6), (5, 7), (6, 7) 2. n > 2000
2. W W W W W W W W W W W
669

11.9 3. No, we cannot jump to the middle of


1. (a) No simple case a linked list as we can with an array
(b) Arguments larger on each call
3. (a) 1, 2, 3, 4, 6, 9

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

4. (b) The method returns zero


(c) One is called with a Tree object 3.  
 
the other with a Node object 1 3

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

abs, 72 applet methods, 617


abstract, 294–295 appletviewer, 620
abstract data type, 479 Argand diagram, 574
Abstract Windowing Toolkit, 524 argument, 36, 72, 175
access control modifier, 192 command line, 361
accessor method, 238 arithmetic operations, 54–57
Ackermann’s function, 438 arithmetic-logic unit, 2
action command, 554 array
action event, 548 assignment, 314
ActionEvent class, 549 comparison, 314
ActionListener interface, 549, 565 declaration, 318
adapter class, 560 element, 308
address, 3 identifier, 310
ADT index, 308
binary search tree, 511 initialization, 309, 318, 321
binary tree, 506 length, 310
list, 479 multi-dimensional, 317–324
queue, 495 parallel, 327
stack, 493 parameter, 324
algorithm ragged, 321
backtracking, 450–453 string, 360–363
efficiency, 404 ASCII, 20, 594
Euclid’s, 428, 435 assignment statement, 26–30, 65–68
Russian peasant, 167 auxiliary storage, 3
Sieve of Eratosthenes, 341 AWT, 524
alias, 219
ALU, 2 \b, 599
ancestors, 277 backslash, 10, 599
anonymous inner class, 562 backtracking algorithms, 450–453
API, 13, 78, 364, 524 big oh, 404–409
applet, 603, 616–622 BigInteger class, 266

672
INDEX 673

binary numeral, 596 BorderLayout, 540


binary search, 385–388 BufferedReader, 587, 590
binary search tree, 511–513 Circle, 220, 229, 235
binary tree, 503–508 Color, 535
bit, 16 Complex, 221, 228
block, 104 Container, 526
boolean, 19 FileReader, 589
boolean operator, 106–109 FileWriter, 588
BorderLayout class, 540 FlowLayout, 538
browser, 603 Font, 528
bubble sort, 395–399 Fraction, 214, 221, 223, 230,
BufferedReader class, 587, 590 232, 236, 238, 240, 246, 251–
bug, 43 257, 279, 285, 412
button, 537 Graphics, 527, 532
button event, 548–554 GridLayout, 542
byte, 16 inner, 480
byte, 16 InputStreamReader, 586
byte code, 4 Integer, 362
JApplet, 617
call by value, 176 JButton, 537
Carroll, Lewis, 163 JComponent, 526
cast, 28, 59, 82, 281, 292 JFrame, 526
ceil, 74 JLabel, 567
central processing unit, 2 JPanel, 526
char, 19 JTextField, 564
character Math, 72–78
ASCII, 20 MouseAdapter, 560
null, 310 MouseEvent, 555
Unicode, 20 Object, 277, 285
characters Person, 278, 286, 291
arithmetic with, 69 PrintStream, 586
HTML, 607 PrintWriter, 588
charAt, 352 Rectangle, 265
child, 504 Stack, 334, 493
circle, drawing, 533 String, 347–379
class, 6 StringTokenizer, 364–368
ActionEvent, 549 Student, 278, 280, 286, 291
adapter, 560 System, 586
anonymous inner, 562 Timer, 576
BigInteger, 266 TimerTask, 576
674 INDEX

WindowAdapter, 563 delimiter, 365


WindowEvent, 563 descendants, 277
wrapper, 416, 566, 586 do statement, 139–140
class hierarchy, 276 doc comments, 632–637
class method, 246–248 Dodgson, Charles, 163
ClassCastException, 282 double, 19
cocktail shaker sort, 399 downcast, 281
code drawing
byte, 4 shapes, 532–536
machine, 4 text, 524–531
collating sequence, 93
Color class, 535 e, 76, 163
command, 179 Easter, date of, 88
action, 554 efficiency, 404
command line, 361, 592 element, 308
comment, 7 ellipse, drawing, 533
Comparable interface, 414 empty statement, 120, 156
compareTo, 98, 414 empty string, 349
comparison encapsulation, 238
array, 314 entity, 607
object, 240–245 equals, 97, 242, 243, 284
compile-time error, 44 equalsIgnoreCase, 357
compiler, 4 equation
computer system, 2 quadratic, 86
concatenation, 9, 36 Eratosthenes’ Sieve, 341
conditional evaluation, 108, 640 error
constants, 41–42 compile-time, 44
constructor, 230–236, 348, 365 logical, 44
container, 526 overflow, 83
Container class, 526 roundoff, 82
content pane, 526 syntax, 44
control unit, 2 underflow, 83
Conway, John, 575 escape sequence, 11, 21, 599
CPU, 2 escape velocity, 89
currentTimeMillis, 403 Euclid’s algorithm, 428, 435
Euler, Leonhard, 194
dangling else, 114 event
de Morgan’s laws, 108 action, 548
debugger, 85 button, 548–554
declaration, 24 mouse, 555–563
INDEX 675

event-driven program, 528 Font class, 528


exception for statement, 141–146
arithmetic, 56 form feed, 171
ClassCastException, 282 Fraction class, 214, 221, 223, 230,
exclusive or, 111 232, 236, 238, 240, 246, 251–
exit, 363 257, 279, 285
exp, 78
exponent, 19 garbage collector, 486
expression Gardner, Martin, 576
arithmetic, 54 gcd, 428
boolean, 92 GIF, 612
conditional, 644 Goldbach’s Conjecture, 194–202
relational, 92–95 Goldbach, Christian, 194
expression tree, 509 graphical user interface, 523
extends, 276–277 Graphics class, 527, 532
extension, file, 14 GridLayout class, 542
GUI, 523
\f, 171, 599
factorial, 163, 438 Hamilton, William Rowan, 267
Fibonacci sequence, 468 Hanoi, towers of, 439
field, 214 hardware, 2
class, 249 header, 7
initialization, 215 helper method, 448, 459, 470
instance, 215 hexadecimal numeral, 596
FIFO, 495 hiding information, 236–239
file, 3 high-level languages, 4
copying, 591 Hindu-Arabic numeral, 376
extension, 14 Hoare, C. A. R., 455
input, 589 Horner’s rule, 471
organization, 257 HTML, 603–622, 632
output, 588 characters, 607
FileReader class, 589 entity, 607
FileWriter class, 588 HTML tags
final, 41 anchor, 614
float, 19 body, 605
floor, 74 colour, 613–614
flow chart, 101, 103, 134, 135, 139, comments, 605
142 container, 604
FlowLayout class, 538 end, 605
font, 528 file, 605
676 INDEX

head, 605 ActionListener, 549, 565


images, 612–613 Comparable, 414
links, 614–616 MouseListener, 555
lists, 609–612 MouseMotionListener, 556
start, 604 WindowListener, 563
text styles, 606–609 interpreter, 4

I/O, 585 J2SE, 13


I/O devices, 3 JApplet class, 617
IDE, 13, 361, 592 Java Foundation Classes, 524
identifier, 22–25 java.io package, 588
array, 310 Javadoc, 199, 632–638
reserved, 23 JButton class, 537
if statement, 101–104, 111–114 JComponent class, 526
immutable, 33, 349, 369 JFC, 524
import, 368, 525 JFrame class, 526
indentation, 8, 101, 113 JLabel class, 567
index, 308, 352 JPanel class, 526
indexOf, 353 JPEG, 612
infinite loop, 136 JTextField class, 564
inheritance, 276–286 JVM, 4
initialization, 27, 217, 309, 318, 349
field, 215 k-sorted, 400
inner class, 480, 562
inorder, 507 layout
input devices, 3 border, 540
input/output, 585–592 flow, 538
In/Out, 578–583 grid, 542
text fields, 564–571 layout managers, 537–547
InputStreamReader class, 586 lazy evaluation, 108, 640
insertion sort, 389–391 leaf, 504
instance method, 221–227 leap year, 115
instanceof, 291 length, 310, 351
int, 17 lexicographic order, 98
Integer, 362 Life, game of, 575
integer types, 16–18 line segment, drawing, 532
integrated development environment, linear list, 478
13 linked list, 478–490
interactive program, 39 list
interface, 412 FIFO, 495
INDEX 677

LIFO, 493 exit, 363


linear, 478 hasMoreTokens, 367
linked, 478–490 helper, 448, 459, 470
pushdown, 493 Horner’s, 471
queue, 495–497 instance, 221–227
recursive processing, 498–502 main, 7
stack, 492–495 Math
traversal, 484 abs, 72
listener, 548 ceil, 74
log, 78 exp, 78
logical error, 44 floor, 74
long, 17 log, 78
max, 74
machine code, 4 min, 74
magic numbers, 42 pow, 73
magic square, 340 random, 76
Mandelbrot set, 574 round, 74
mantissa, 19 sqrt, 73
markup language, 604 trigonometric, 76
mask, 643 nextToken, 367
Math class, 72–78 parseDouble, 363
Math.E, 76 parseInt, 362
Math.PI, 76 query, 179
matrix, 340 signature, 185
max, 74 String
median, 342, 392 charAt, 352
memory, 3 compareTo, 98
merge sort, 474 equals, 97
method equalsIgnoreCase, 357
accessor/mutator, 238 indexOf, 353
applet, 617 length, 351
argument, 36, 72, 175 substring, 354
boolean, 187 toLowerCase, 357
class, 246–248 toUpperCase, 357
command, 179 trim, 355
compareTo, 414 valueOf, 358
constructor, 230–236, 348 stub, 560
countTokens, 367 toString, 243, 284, 290, 293,
currentTimeMillis, 403 357
equals, 97, 242, 243, 284 min, 74
678 INDEX

modular programming, 169 associativity, 647


mouse event, 555–563 bitwise, 641
MouseAdapter class, 560 boolean, 106–109, 640
MouseEvent class, 555 concatenation, 9, 36
MouseListener interface, 555 increment/decrement, 62
MouseMotionListener interface, 556 instanceof, 291
multi-threading, 527 precedence, 54, 646
multiple inheritance, 276 relational, 93
mutator method, 238 summary, 640–647
order
\n, 11, 21, 599 lexicographic, 98
NaN, 56 output devices, 3
nesting, 111 overflow error, 83
nibble, 18 overloading, 297
node, 478 method, 184
null, 217, 349 operator, 60
null character, 310 overriding, 243, 284, 297
number
perfect, 161 package, 78, 192, 257, 525
prime, 161, 194 java.io, 588
social insurance, 164 java.util, 368
number representation, 594–598 package visibility, 236
numeral palindrome, 375
base x, 595 parallel array, 327
binary, 596 parameter, 174–185
hexadecimal, 596 array, 324
Hindu-Arabic, 376 implicit, 222
octal, 597 reference vs. primitive, 225
Roman, 376 parent, 504
parseDouble, 363
O, 404–409 parseInt, 362
object, 31 parsing, 587
array of, 326–329 pendulum, 79
comparing, 411–418 perfect number, 161
comparison, 240–245 permutation, 447
Object class, 277, 285 Person class, 278, 286, 291
object program, 4 π, 76
octal numeral, 597 pixel, 526
operator plus/minus, unary, 54
arithmetic, 54 polygon, drawing, 534
INDEX 679

polymorphism, 293, 418 reading, 35–40


polynomial, 339 rectangle, drawing, 532
evaluation, 471 recursion
Tchebyshev, 469 everyday, 424–425
postorder, 507 in Mathematics, 427–429
pow, 73 with lists, 498–502
power series, 163 with strings, 446–449
precedence, 54, 66, 67, 82, 107 with trees, 506
preorder, 507 redirection, 592
prime number, 161, 194, 341 reference, 32, 215
primitive types, 16–21 relational expression, 92–95
printing reserved word, 23
basic, 6–11 return value, 72
expression values, 59–60 RGB, 535
values of variables, 35–40 Roman numeral, 376
PrintStream class, 586 root, 504
PrintWriter class, 588 round, 74
private, 192, 237, 282, 298 roundoff error, 82
program Russian peasant algorithm, 167
development, 13–15
event-driven, 528 Samoyed, 276
interactive, 39 scientific notation, 19
object, 4 scope, 189–193
source, 4 SDK, 13, 632
prompt, 39 search
protected, 282, 298 binary, 385–388
pseudo-code, 194 sequential, 382–384
public, 7, 192, 236, 257 selection sort, 392–394
pushdown store, 493 sentinel, 136
Pythagorean triplet, 155 sequence
Fibonacci, 468
quadratic equation, 86 sequential search, 382–384
quaternion, 267–271 shadowing, 261, 283, 298
query, 179 Shellsort, 400–402, 408
queue, 495–497 short, 17
quicksort, 455–460 short circuit evaluation, 108, 640
sibling, 504
\r, 599 side effect, 67
ragged array, 321 Sieve of Eratosthenes, 341
random, 76 signature, 185
680 INDEX

SIN, 164 stub method, 560


software, 2 Student class, 278, 280, 286, 291
software development kit, 13 subclass, 276
sort substring, 354
bubble, 395–399 subtree, 504
cocktail shaker, 399 Sun Microsystems, 5, 13, 78, 571,
insertion, 389–391 632, 633
merge, 474 super, 288–291, 551
quick, 455–460 superclass, 276
selection, 392–394 Swing, 524
Shell, 400–402, 408 switch statement, 116–117
stable, 392 syntax error, 44
source program, 4 System class, 586
sqrt, 73
square, drawing, 532 \t, 599
stable sort, 392 Tchebyshev polynomial, 469
stack, 492–495 text editor, 13
statement, 7 this, 223, 231, 235, 260
assignment, 26–30, 65–68 thread, 528
compound, 104 tic-tac-toe, 575
do, 139–140 Timer class, 576
empty, 104, 120, 156 TimerTask class, 576
for, 141–146 title bar, 526
if, 101–104, 111–114 token, 365
import, 368 toLowerCase, 357
nested, 111 toString, 243, 284, 290, 293, 357
return, 173 toUpperCase, 357
switch, 116–117 towers of Hanoi, 439
while, 134–138 tracing, 85, 122, 157, 204, 371, 442,
static, 7 467
stream, 586 transpose, 340
string traversal
concatenation, 9, 36 list, 484
empty, 349 tree, 507
methods, 351–358 tree
printing, 7 binary, 503–508
variable, 31–34 binary search, 511–513
String class, 347–379 expression, 509
StringBuffer, 349 terminology, 504
StringTokenizer class, 364–368 traversal, 507
INDEX 681

traversal orderings, 507


triangle
area of, 84
trigonometric methods, 76
trim, 355
type
boolean, 21
cast, 28
character, 20
conversion, 27
floating point, 19
integer, 16
primitive, 19

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

web browser, 603


while statement, 134–138
white space, 355, 366
WindowAdapter class, 563
WindowEvent class, 563
WindowListener interface, 563
Windows, 524
wrapper class, 416, 566, 586

You might also like