CMPU 102, Fall 2005 Lecture 1

Outline

  1. Overview of Course
  2. Review of Primitive Java

Overview of the course

In this course we will study

  1. fundamental data structures and algorithms
  2. object-oriented programming

Data structures

All tasks performed by compters require manipulating data.  A data structure is a way of arranging data so it can be accessed and modified efficiently.  Some examples of data structures include arrays, linked lists, trees, heaps, hash tables, and graphs.  We will examine these and many other kinds of data structures in detail in this course.

Algorithms

An algorithm is an automatic procedure for solving some problem.  By automatic we are talking about procedure that can be carried out by a computer.  Many algorithms operate on a data structure: for example, sorting the elements in an array---arranging them in ascending or descending order---would be carried out by a sorting algorithm.  Usually, there are lots of different variants of an algorithm to perform a particular task.  For example, bubble sort, heap sort, and merge sort are all examples of sorting algorithms. 

There are two things we would generally like to know about an algorithm: given a problem of a given size

Analysis of algorithms allows us to rigorously answer these questions.  Selecting efficient algorithms for the problem you want to solve is one of the keys to writing efficient programs.

Object-oriented programming

Programs can get very complicated very quickly.  Without an effective way to structure them, writing programs of any complexity can be unmanageable.

Object-oriented programming is a general approach to managing the complexity of writing programs.  The basic idea is that your program is composed of objects.  An object contains some data, and also supports operations to access or manipulate the data.  Objects provide a very natural way to express solutions for many kinds of programming problems.  We will explore object-oriented design and programming more fully later in the course.

Java and Eclipse

In this course we will be using the Java programming language.  You will probably want to download the Java Development Kit (JDK) for your own computer.  You can do so from the web page http://java.sun.com/j2se/1.5.0/download.jsp.  Make sure you follow the link labeled "Download JDK 5.0 Update n" (for whatever the current value of n is).

We will also be using the Eclipse Integrated Development Environment, version 3.1.  You can download Eclipse from http://www.eclipse.org/downloads/index.php.  Click on the link labeled "Other downloads for 3.1.", and then click on the download that matches your system type: for example, "Mac OSX", "Linux x86/GTK 2", or "Windows".

Both Java and Eclipse are installed on the Sun workstations in the Asprey Advanced Computation Lab.  This is where our lab sessions will meet, and you may work on your assignments there as well.

Where this course fits

In Computer Science I you learned the fundamentals of procedural programming using Java, and were introduced to object-oriented concepts (classes and inheritance, etc.)  In this class we will build on this foundation by studying the fundamental data structures, algorithms, and software construction techniques that enable us to create powerful and sophisticated programs.  Upon completing this course you will be able to write programs to accomplish just about anything that is computable.

Although this course involves substantial programming, the real focus of the course is on data structures and algorithms, many of which are elegant and beautiful in a mathematical sense.  The applications made possible by these data structures an algorithms, such as the internet, web search engines like Google, movies and video games, are a part of daily life.

Computer Science is becoming increasingly interdisciplinary.  Computing has applications in all areas of human endeavor, such as the sciences, engineering, the arts and humanities, music, etc.  Your experience in this course should be relevant no matter what you decide to do next.  Of course, we certainly encourage you to consider continuing in Computer Science.

Review of Primitive Java

Compilation and Runtime Model

Recall the basic operation of a computer.  The computer's Central Processing Unit (CPU) executes machine instructions loaded from the computer's memory.  Instructions, or combinations of instructions, can load or store values in memory, perform computations on those values, and perform input and output (such as reading data from the keyboard, a file on disk, or a network connection.)

Each "CPU family" accepts a different machine language.  For example, AMD and Intel Pentium CPUs use the same machine language, usually referred to as "x86".  However, Sun workstations use SPARC CPUs, which use a different machine language.  A program written in x86 machine language will not run on a Sun workstation, nor will a SPARC machine language program run on a x86 PC.

Most traditional programming languages work as follows: programs written in a high level language (such as C or C++) are fed to a compiler program which translates the high level language into machine instructions for a particular CPU family.  The output of the compiler is an object file, which contains compiled functions and data, but is not ready to execute.  A linker program takes one or more object files and prepares them to be loaded into memory and executed.  The output of the linker is an executable, which as the name suggests is ready to load into memory and execute.  As an example, here is how a program written in the C programming language might be compiled and linked to produce an executable.

The execution model for Java is somewhat different.  Java programs run on a Java Virtual Machine, or JVM.  A JVM is an executable program, but its job is to act as a CPU for executing Java programs.  The machine language of the Java Virtual Machine is called Java bytecode.  In most important ways, Java bytecode is just like a machine language for a traditional hardware CPU.  Like C and C++, a compiler translates programs written in Java into bytecode, which is stored in Java class files.  When the program is ready to be run, the class files are loaded by the JVM, and the bytecode instructions they contain are executed.

In order to execute bytecode instructions, they must be translated into machine instructions that can be executed on the real (hardware) CPU.  This can be done in two ways.  First, an interpreter in the JVM can simulate the bytecode instructions one at a time.  This is easy, but slow.  A faster but more complicated approach is to use dynamic compilation: inside the JVM, a second compiler translates larger chunks of Java bytecode into hardware machine instructions.  All modern JVMs use dynamic compilation.

The big advantage of Java over languages like C and C++ is that a Java program (composed of Java class files) can run on ANY system that has a JVM.  You don't have to recompile your Java program for every kind of computer you want it to run on.  Java also happens to be a safe programming language, because certain kinds of low-level errors are automatically caught and handled, rather than crashing the program or (worse) silently corrupting the program.  In general this property makes Java programs much easier to debug than C or C++ programs.

You might think that all of the extra work going on inside a JVM would make Java programs run more slowly than C or C++ programs.  In fact, for most programs, Java is just as fast as C or C++.  The main difference is that a Java program running on a JVM takes slightly longer to start up and requires more memory than an equivalent C or C++ program.

Primitive Java data types

The primitive Java data types are the most basic kinds of data that a Java program can use.

Data type Range
boolean the constants true and false
byte integers -128 to 127
char all UTF-16 Unicode characters
short integers -32,768 to 32,767
int integers -2,147,483,648 to 2,147,483,647
long integers -263 to (263 - 1)
float 32 bit floating point values, approx 10-46 to 1038, positive and negative
double 64 bit floating point values, approx 10-324 to 10308, positive and negative

Note that arithmetic involving float and double values is inherently imprecise.  You should think of floating point values as being somewhat "fuzzy".

Constant values

Specific primitive values, or constants, can be expressed using notation that depends on the type of constant value.

Kind of constant Type Example
Decimal int 42
Octal (base 8) int 0377
Hexidecimal (base 16) int 0xDEADBEEF
Floating point double 3.1415
Floating point float 3.1415F
Character char 'q'
String java.lang.String "Hello, world"

Note that strings are not a primitive type.  Rather, they are objects which are instances of the java.lang.String class.  We will cover objects and classes later in the course.

Variables and assignments

A variable is a single memory location that contains a value of a particular type.  A variable is declared by a statement specifying the type of the variable and the name of the variable.  Variables can be assigned a value using the assignment operator "=".  All variables must be assigned a value before the variable can be read.  Often, variables are assigned a value at the point where they are declared.

int numberOfCookies = 10;
double approximationOfPi = 3.1415926535;
char firstInitial = 'D';
String greeting = "Hi there";

The value of one variable can be assigned to another variable:

int a;
int b;

a = 10;    // assign "a" the value 10
b = a;     // now "b" also has the value 10

Basic output

You can print the value of a constant or variable by passing it to the System.out.println method:

System.out.println("Hello");
System.out.println(42);
You may print out a sequence of strings and other values by joining them with the "+" operator:
String month = "September";
int day = 1;
int year = 2005;
System.out.println("Today's date is " + day + " " + month + " " + year);
// Prints out "Today's date is 1 September 2005"

Operators

Variables and constant values can be combined using operators which compute a new value.

Arithmetic operators:

Operator Computation Result type Example
+ Addition depends on operands (int, float, or double) a + b
- Subtraction depends on operands (int, float, or double) a + b
* Multiplication depends on operands (int, float, or double) a * b
/ Division depends on operands (int, float, or double) a / b
% Modulus (remainder) int a % b

Sequences of operators and operands can be built up to form more complicated expressions.  For example:

int a = (b + c) * d;
Parentheses should be used to ensure that each part of the expression is evaluated in the intended order.  For example, in the example above, the parentheses ensure that the addition (b + c) occurs before the multiplication.

More assignment operators

All arithmetic operators can be combined with assignment as follows:

int a = 5;
a += 1; // same as "a = a + 1"

int b = 6;
b *= 10; // same as "b = b * 10"

Increment and decrement operators

Integer variables (types byte, char, short, int, long) can use the increment and decrement operators ++ and --.  These add or subtract 1 from the current value of the variable.  Each has two forms: pre-increment and pre-decrement, and post-increment and post-decrement.

int a = 5;
System.out.println(a++);  // prints "5"
System.out.println(a);    // prints "6"

int b = 5;
System.out.println(++b);  // prints "6"
System.out.println(b);    // prints "6"
As a matter of style, you should use the increment and decrement operators sparingly.  Generally, they are only used in for and while loops.

Comparison operators

Comparison operators compute the equality or inequality of two values.  The result of a comparison is always boolean.

Operator Computation Example
== Equality a == b
!= Inequality a != b
< Less than a < b
> Greater than a > b
<= Less than or equal to a <= b
>= Greater than or equal to a >= b

Logical operators

The logical operators && and || compute a boolean value based on the values of two boolean operands.

a b a && b a || b
true true true true
true false false true
false true false true
false false false false

If statements

General forms of an if statement:

if (boolean expression) {
    statements
} else {
    statements
}

if (boolean expression) {
    statements
}
The second form is equivalent to having an explicit, empty "else" block.  Note that you can omit the curly braces ("{" and "}") if there is only a single statement in the "if" or "else" block.  However, you can avoid mistakes by always using curly braces.

Examples:

if (dayOfMonth < 15) {
    System.out.println("First half of the month");
}

Loops

Loops repeatedly execute a sequence of statements - the loop body - until the loop termination condition is met.  Each execution of the loop body is referred to as an iteration.

While loop:

while (boolean expression) {
    statements
}
Example:
int count = 10;
while (count > 0) {
    System.out.println(count);
    count--;
}
System.out.println("Lift off!");

For loop:

for (initialization stmt; boolean expr; increment/decrement stmt) {
    statements
}
The initialization statement is executed once, before the loop is entered.  Typically, a loop variable will be declared and initialized as the initialization statement.  The increment/decrement statement is executed after each execution of the loop body.  Otherwise, for loops are equivalent to while loops.  Example:
for (int count = 10; count > 0; count--) {
    System.out.println(count);
}
System.out.println("Lift off!");

Do/while loops are like while loops, except that the loop condition is checked after each execution of the loop body, rather than before.  This means that the loop body is guaranteed to be executed at least once.  Example:

int count = 10;
do {
    System.out.println(count);
    count--;
} while (count >= 1);
System.out.println("Lift off!");
Do/while loops tend to be fairly rare.  You probably won't want to use them very often.

break and continue

The break statement allows you to terminate a loop early.  Example:

int count = 10;
while (true) {
    if (count == 0) {
        break;
    }
    System.out.println(count);
}
System.out.println("Lift off!");

The continue statement allows you to finish the current iteration of the loop early.

// Print even numbers from 0..100
for (int count = 0; count <= 100; count++) {
    if (count % 2) == 1) {
        // This is an odd number. Skip it.
        continue;
    }
    System.out.println(count);
}

As a matter of style, break and continue statements should be used sparingly.  Generally, you should only use them when they allow you to write simpler code.

Methods

A Java method is the same thing as a function or procedure in other languages.  It takes some number of parameters as input, performs a computation, and optionally returns a result value.

// A method to add two integers and return the result
public static int add(int a, int b) {
    int result = a + b;
    return result;
}

The "public" keyword means the method may be called from anywhere in the program.  If we used the "private" keyword, then the method would only be visible inside the class the method belongs to.  The "static" keyword means the method is not an operation to be performed on a specific object.  We will study classes and objects later in the semester.