Introduction
Supplemental material
Getting started with Java
Let's get warmed up with a few simple Java exercises:
In-Class Exercise 1:
Write a class HelloWorld that runs a "Hello World" program which prints a string out (perhaps
your name instead of HelloWorld) to the screen. Compile and
run at the command-line.
In-Class Exercise 2:
Download Wall.java and
and ImageTool.java.
- Compile to make sure both compile.
- Use a search engine to find a JPEG image of a brick wall (or
any other kind). Download that to the same directory, saving
it as wall.jpg.
- Now execute Wall - you should see your image.
- Next, examine the Wall.java program and use
g.drawLine() commands to write your initials on the wall.
All you have to do is figure out a set of such commands to draw
your initials as line segments.
In-Class Exercise 3:
Download and modify this Java program
so that it prints out patterns that look like this (when N=5):
1
2 1
3 2 1
4 3 2 1
5 4 3 2 1
If you need to review Java:
Note: we will be using a recent version of Java but will not use
the more advanced or exotic features
About English Playing Card Data
There are number of ideas that are better understood by physically manipulating data. We could try to model this with all sorts of objects, but we will use a set of English Playing Cards in some examples and exercises to encourage you to "play along" with your own cards to help you interact with the mechanics of the algorithms. This section is provided to help us maintain the same understanding of how these types of cards are counted and ranked in this material.
- English playing cards consist of 4 suits: Spades, Hearts, Diamonds, and Clubs.
- Each suit has 13 values which can be ranked from Ace (1st) to King (13th). In this course, the ranking order of values will always be A,2,3,4,5,6,7,8,9,10,J,Q,K.
- Card ranking is a convention subject to rules of a particular game. In Poker, an Ace is ranked highest and a Deuce (2) is ranked as lowest. However, manufacture standard packing suggests the default order where, an Ace has the lowest rank (1st) and Kings have the highest rank (13th) and therefore Jacks are ranked 11th and Queens are ranked 12th.
- The suit ranking order is Spades (Highest), Hearts (2nd), Diamonds (3rd), Clubs (Lowest).
- For card values, a card value of 1 or 2 is "low" and a card value of Q or K is "high". The card value ranking convention is ascending rank order.
- For card suits, the suits are typically listed as Spade, Heart, Diamond, Club; however, a Spade is considered "high" rather than "low" yet it is in the left-most place of the list like the Ace for values. In other words, the card suit ranking convention is descending rank order.
- There is other data associated with a playing card, i.e. color, but color is different than value and suit. Color is strictly depedent on suit, Spades and Clubs are black while Hearts and Diamonds are red. Due to this dependence, suit clearly indicates the color but suit cannot be determined from color.
The above analysis should inform you about class designs should you create a PlayingCard class like for example PlayingCard.java and PlayingCardExample.java.
What the course is about
In brief:
- Data structures: arrays, lists, queues, stacks, trees.
- Memory models of these data structures.
- Problem-solving: recursion, using data structures.
- Software skills: harder programs, trickier code, debugging.
- Algorithm analysis: reasoning about program execution time.
There a few themes for this course that drive its structure and topics:
- We will come to understand organization and efficiency in space (memory) and time (computation)
- The first half of the course will heavily emphasize organization in space.
- Efficiency in time will be explicitly formalized in the second half of the course; however, time efficiency will be central to all decisions throughout the entire course, so keep it in mind.
- The above will be framed in terms of two fundamental algorithms: search and sort
- Search attempts to answer two questions: the existential (Does this item exist within the data structure?) and the positional (Where does the item exist in the data structure?)
- Sort means to put data in order and data is typically sorted because we can search faster if data is ordered; however, a sort is slower than search so sort can actually slow down search.
- This cost in terms of time will drive a lot of decisions about why we might choose one data structure over another.
Some examples of a simple list
What exactly is a list?
⇒
A data structure to hold, well, a list of items.
Some abstract list applications are...
- A shopping list
- A media play list for songs, shows, or movies
- A list of chapters in a book
- A hand of playing cards
|
|
Here is an example of how we can use the built-in Java linked-list:
// Need to import the LinkedList class from java.util:
import java.util.*;
public class ListExample {
public static void main (String[] argv)
{
// Create an empty list:
LinkedList<Integer> intList = new LinkedList<Integer> ();
// Produce data and add each to list.
for (int i=0; i < 10; i++) {
// Compute i-th square number.
int square = i*i;
intList.add (square);
}
// Print.
for (int i=0; i < 10; i++) {
int element = intList.get (i);
System.out.println ("Element #" + i + " = " + element);
}
}
}
Note:
- The data structure above is LinkedList<Integer>,
from the Java library.
- We haven't looked "under the hood" to see how it works; we
merely use it above.
- The example shows how to create a list, how to add stuff and
how to extract stuff in order.
Lists can contain any kind of object, for example:
import java.util.*;
import java.awt.*;
import java.awt.image.*;
public class ListExample2
{
public static void main (String[] argv)
{
// Create an empty list that can hold objects of type Image:
LinkedList<Image> imageList = new LinkedList<Image> ();
// Retrieve images from files using ImageTool, and add one
// by one into the list.
ImageTool imTool = new ImageTool ();
Image image1 = imTool.readImageFile ("alum1.jpg");
imageList.add (image1);
Image image2 = imTool.readImageFile ("alum2.jpg");
imageList.add (image2);
Image image3 = imTool.readImageFile ("alum3.jpg");
imageList.add (image3);
viewImages (imageList);
}
// The list as method parameter:
static void viewImages (java.util.List<Image> imageList)
{
ImageTool imTool = new ImageTool ();
for (Image image : imageList) { // Note the for-loop
imTool.showImage (image);
}
}
}
In-Class Exercise 4:
Download the above program,
along with ImageTool.java and the
three image files (alum1.jpg,
alum2.jpg,
alum3.jpg), compile and execute.
Can an array be used for a list?
⇒ Yes, for example
public class ListExample3 {
public static void main (String[] argv)
{
// Plain old array: must specify size
int[] intList = new int [10];
for (int i=0; i < 10; i++) {
intList[i] = i*i;
}
// Print.
for (int i=0; i < 10; i++) {
System.out.println ("Element #" + i + " = " + intList[i]);
}
}
}
Note:
- When using an array, we must specify the size in advance.
- For some applications, we may not know the desired size
ahead of time.
- Later we will see that arrays can be very efficient.
A queue
What is a queue?
- In a list:
- We usually add items at the end, but sometimes also in
sorted order.
- Deletion is usually an afterthought, to "fix" the list.
- Access is usually front to back, or for a particular element
in the list.
- In a queue:
- Items are always added at the end.
- Items are always deleted from the beginning.
- The whole purpose is to create "order-of-arrival".
Let's look at an example
import java.util.*;
public class QueueExample {
public static void main (String[] argv)
{
// We'll use a list, but treat it like a queue.
// Note: it's a list meant to hold strings.
LinkedList<String> queue = new LinkedList<String> ();
// Add some names:
queue.add ("Alice");
queue.add ("Bob");
queue.add ("Chen");
queue.add ("Dagmar");
queue.add ("Eva");
queue.add ("Faisal");
System.out.println ("Queue contents: " + queue);
// Extract one by one:
while (! queue.isEmpty() ) {
String name = queue.remove ();
System.out.println ("Next comes " + name);
System.out.println ("Queue contents: " + queue);
}
}
}
Note:
- The output is:
Queue contents: [Alice, Bob, Chen, Dagmar, Eva, Faisal]
Next comes Alice
Queue contents: [Bob, Chen, Dagmar, Eva, Faisal]
Next comes Bob
Queue contents: [Chen, Dagmar, Eva, Faisal]
Next comes Chen
Queue contents: [Dagmar, Eva, Faisal]
Next comes Dagmar
Queue contents: [Eva, Faisal]
Next comes Eva
Queue contents: [Faisal]
Next comes Faisal
Queue contents: []
- We've used three types of operations:
add(), isEmpty(), remove()
- It's possible to print some data structures in their
entirety with a single println:
System.out.println ("Queue contents: " + queue);
Of course, this may not be a good idea if the queue is large (e.g.,
100,000 items).
A stack
What is a stack?
- You always add items to the front (not the back).
- You always remove items from the front
⇒ you remove the most recently inserted item.
Let's look at an example:
import java.util.*;
public class StackExample {
public static void main (String[] argv)
{
// The Java library has a Stack data structure. Here, we'll
// make a stack to hold strings.
Stack<String> toDoList = new Stack<String> ();
// Add some strings.
toDoList.push ("Pay bills");
toDoList.push ("Clean room");
toDoList.push ("Do homework");
toDoList.push ("See movie");
toDoList.push ("Hang out");
// Print.
System.out.println ("Priorities: ");
while (! toDoList.isEmpty() ) {
String nextPriority = toDoList.pop ();
System.out.println (" " + nextPriority);
}
}
}
Note:
- The output is, as you might expect:
Priorities:
Hang out
See movie
Do homework
Clean room
Pay bills
- The operations we've used are: push(), pop(), isEmpty().
- Although we have created a stack of strings, we could just
as easily create and use a stack of integers or various kinds of objects.
A tree
What is a tree?
- This a data structure, we'll learn later, that resembles
the branch-like structure of a tree.
- Trees are a fundamental data structure in computer science.
- There are many variants of trees; we'll learn about
Binary Trees in this course.
- Why use trees at all?
⇒ They are simple and efficient.
Consider this example:
public class TreeExample {
public static void main (String[] argv)
{
// Java's library has tree variant called TreeSet.
// We'll make an instance to hold String's.
TreeSet<String> unusualWords = new TreeSet<String> ();
// Put some stuff in.
unusualWords.add ("queueing"); // Six vowels in a row.
unusualWords.add ("psychorhythms"); // Longest with one vowel.
unusualWords.add ("verisimilitudes"); // Longest with alternating vowel/consonant.
unusualWords.add ("frillless"); // Three letters in a row.
unusualWords.add ("bookkeeper"); // Three double-letter's in a row.
unusualWords.add ("cwm"); // Pronounced "koom" (type of crevasse).
unusualWords.add ("feedback"); // Contains all letters 'a' to 'f'.
// Print.
System.out.println (unusualWords);
}
}
Note:
- The program prints out:
[bookkeeper, cwm, feedback, frillless, psychorhythms, queueing, verisimilitudes]
Thus, the output looks like a simple list. It's the internal
structure that's tree-like.
- Like other data structures, this one can hold numbers or any
kind of object.
In-Class Exercise 5:
In this exercise, you will compare the speed of searching in
a tree vs. a straightforward list.
Download TreeExample2.java
and UniformRandom.java.
You will see code that creates a tree and a linked-list and
sets them up for a search comparison. The code for searching in the
tree is already given. You need to add very similar code for
searching in the list. Then, compile and execute.
What the course is really about
Now that we've seen some examples, you have a better sense of what's
forthcoming:
- We will learn how fundamental data structures work, how they
are used, and how to build them ourselves.
- We will learn why they work, for example, why one is
faster/better than another.
- We will learn how data structures are represented in memory.
- We will strengthen our programming skills and learn more
about Java to be able to implement data structures.
- Along the way, we will also learn about problem-solving
and recursion.
An important course note
The examples in this introduction use built-in Java data structures; however, we
will be building and studying these data structures ourselves. After
this introduction, we no longer refer to the built-in Java data structures
and you may not use the Java built-in data structures in your work.
Reasoning:
- You may work in an area that prevents you from using Java or sophisticated languages with built-in data structures.
- Systems development typically requires a more fundamental language like C which has no built-in data structures.
- Small devices such as smart house devices or wearables do not typically support Java and are typically programmed with more fundamental languages like C.
- New languages may require the development of data structures for the language to motivate user adoption.
- We must make expert programming decisions. Computer scientists need to know which data structure is best for a particular application.
- The efficiency of some operations such as search or sort can vary wildly depending on the data structure and the algorithm used.
- The fastest algorithms perform optimally only when the data is structured according to strict constraints, like being in order.
- Poor data structuring tends to lead to bad to worse cases of time and space performance, in other words, a slow program that needs too much memory.
© 2006-2020, Rahul Simha & James Taylor (revised 2021)