|
First Summer 2004
|
Lecture Notes Twenty-Three:
Vectors, Hashtables, Leftovers.
First here's a Selection Sort that we have developed in class.
class One {
static void sort(int[] a) {
for (int start = 0; start < a.length - 1; start++) {
for (int j = start; j < a.length; j++) {
if (a[start] > a[j]) { // sorting in ascending order
int temp = a[start];
a[start] = a[j];
a[j] = temp;
}
}
}
}
public static void main(String[] args) {
int[] numbers = new int[args.length];
for (int i = 0; i < numbers.length; i++) {
numbers[i] = Integer.parseInt(args[i]);
}
System.out.println("Here's the initial array: ");
One.show(numbers);
System.out.println("Let me sort it in ascending order...");
One.sort(numbers);
System.out.println("... Done\nHere it is sorted: ");
One.show(numbers);
}
static void show(int[] a) {
for (int i = 0; i < a.length; i++) {
System.out.print(a[i] + " ");
}
System.out.println();
}
}
Here's how it runs:
frilled.cs.indiana.edu%javac One.java
frilled.cs.indiana.edu%java One 3 4 1 2 6 7
Here's the initial array:
3 4 1 2 6 7
Let me sort it in ascending order...
... Done
Here it is sorted:
1 2 3 4 6 7
frilled.cs.indiana.edu%
To clarify the idea of scope of a variable let's look at this example:
class Two {
public static void main(String[] args) {
int a = 1;
{
int b = 2;
System.out.println("a: " + a);
a = 4; // change will be seen by the other block
System.out.println("b: " + b);
}
// System.out.println("b: " + b);
{
int b = 3;
System.out.println("a: " + a);
System.out.println("b: " + b);
}
}
}
I'll let you experiment with it.
Here's a second method of sorting: Bubble Sort.
class Three {
static void sort(int[] a) {
boolean done;
do {
done = true;
for (int i = 0; i < a.length - 1; i++) {
if (a[i] > a[i + 1]) { // sort in ascending order
int temp = a[i];
a[i] = a[i + 1];
a[i + 1] = temp;
done = false;
}
}
} while (! done);
}
static void main(String[] args) {
int[] numbers = new int[args.length];
for (int i = 0; i < numbers.length; i++) {
numbers[i] = Integer.parseInt(args[i]);
}
System.out.println("Here's the initial array: ");
Three.show(numbers);
System.out.println("Let me sort it in ascending order...");
Three.sort(numbers);
System.out.println("... Done\nHere it is sorted: ");
Three.show(numbers);
}
static void show(int[] a) {
for (int i = 0; i < a.length; i++) {
System.out.print(a[i] + " ");
}
System.out.println();
}
}
It runs just like the other one, but the mechanism is different.
The outer do-while
loop is normally added at the end.
For this reason I find the following method much more intuitive.
class Four {
static void sort(int[] a) {
boolean done = true;
for (int i = 0; i < a.length - 1; i++) {
if (a[i] > a[i + 1]) { // sort in ascending order
int temp = a[i];
a[i] = a[i + 1];
a[i + 1] = temp;
done = false;
}
}
if (! done)
sort(a);
}
static void main(String[] args) {
int[] numbers = new int[args.length];
for (int i = 0; i < numbers.length; i++) {
numbers[i] = Integer.parseInt(args[i]);
}
System.out.println("Here's the initial array: ");
One.show(numbers);
System.out.println("Let me sort it in ascending order...");
One.sort(numbers);
System.out.println("... Done\nHere it is sorted: ");
One.show(numbers);
}
static void show(int[] a) {
for (int i = 0; i < a.length; i++) {
System.out.print(a[i] + " ");
}
System.out.println();
}
}
Yes, recursion, in some cases, amounts to just a loop.
One could improve on the Bubble Sort technique above.
Here's an
applet that illustrates the differences between three sorting methods:
- quick sort (not selection sort)
- regular bubble sort
- improved bubble sorting
The applet, I believe, is very exciting.
It shows how the methods work, and illustrates topics from chapter 15.
Let's move on.
Last time we talked about
- inheritance and
- the class extension mechanism.
These are more or less the same topic.
We can illustrate them both by this example:
class Horse {
int numberOfLegs();
void fun() {
System.out.println("I am a Horse.");
}
}
class Unicorn extends Horse {
void fun() {
System.out.println("I am a Unicorn. Like a horse, but with a horn.");
}
}
class Experiment {
public static void main(String[] args) {
Horse a = new Horse();
System.out.println("A horse has " + a.numberOfLegs + " legs.");
Horse b = new Unicorn();
Unicorn c = new Unicorn();
System.out.println("A unicorn has " + c.numberOfLegs + " legs.");
// Unicorn d = new Horse(); // Not allowed.
System.out.print("First test: ");
a.fun(); // a horse, of course
System.out.print("Second test: ");
c.fun(); // a unicorn, of course
System.out.print("Third test: ");
b.fun(); // what will this be?
}
}
That's what you need to know to understand applets.
It's also important to understand how Vector
s work.
We will talk about Vector
s in a little while.
Here's the example promised on developing things in stages:
frilled.cs.indiana.edu%cat Stages.java
class One {
int add(int n, int m) {
if (m == 0) return n;
else return add(n+1, m-1);
}
}
class Two extends One {
int mul(int n, int m) {
if (m == 1) return n;
else return add(n, mul(n, m-1));
}
}
class Three extends Two {
int pow(int n, int m) {
if (m == 0) return 1;
else return mul(n, pow(n, m-1));
}
}
class Calculator {
public static void main(String[] args) {
Three calc = new Three();
int n = 3, m = 5;
System.out.println(n + " + " + m + " = " + calc.add(n, m));
System.out.println(n + " * " + m + " = " + calc.mul(n, m));
System.out.println(n + " ^ " + m + " = " + calc.pow(n, m));
}
}
frilled.cs.indiana.edu%javac Stages.java
frilled.cs.indiana.edu%java Calculator
3 + 5 = 8
3 * 5 = 15
3 ^ 5 = 243
frilled.cs.indiana.edu%
Here's a different, somewhat similar, example on interfaces.
frilled.cs.indiana.edu%cat Example.java
interface Multiplier {
int mul(int n, int m);
}
class Alpha implements Multiplier {
public int mul(int n, int m) {
return n * m;
}
}
class Beta implements Multiplier {
public int mul(int n, int m) {
int result = 0;
for (int i = 0; i < m; i++)
result += n;
return result;
}
}
class Gamma implements Multiplier {
public int mul(int n, int m) {
if (m == 1) return n;
else return n + mul(n, m-1);
}
}
class Example {
public static void main(String[] args) {
Alpha a = new Alpha();
Beta b = new Beta();
Gamma g = new Gamma();
int n = 5, m = 3;
System.out.println(n + " * " + m + " = " + a.mul(n,m) + " (by Alpha)");
System.out.println(n + " * " + m + " = " + b.mul(n,m) + " (by Beta )");
System.out.println(n + " * " + m + " = " + g.mul(n,m) + " (by Gamma)");
}
}
frilled.cs.indiana.edu%javac Example.java
frilled.cs.indiana.edu%java Example
5 * 3 = 15 (by Alpha)
5 * 3 = 15 (by Beta )
5 * 3 = 15 (by Gamma)
frilled.cs.indiana.edu%
An interface is an element of pure design.
|
Now let's talk about Vector s.
|
What is a Vector ?
|
Here's a hint.
|
http://java.sun.com/products/jdk/1.2/docs/api/java/util/Vector.html
Can I make a summary?
|
Go for it.
|
A Vector object responds to the following messages:
|
-
void addElement (Object obj)
-
Adds the object to the end of the vector, increasing its size by one.
-
boolean contains (Object obj)
-
Returns
true
if the object is among the values stored in the
vector, or false
otherwise.
-
Object elementAt (int index)
-
Returns the object at the specified index position.
-
int indexOf (Object obj)
-
Returns the index position of the first instance of the object in the vector
(if the object is in the vector) or
-1
otherwise.
-
int indexOf (Object obj, int index)
-
Same as above, but starts the search from the specified index.
-
void insertElementAt(Object obj, int index)
-
Inserts the object at the specified index position, after shifting
down objects at and below the insertion point.
-
boolean isEmpty ()
-
Returns
true
if the vector contains no objects, or
false
otherwise.
-
void removeElement (Object obj)
-
Removes the first occurrence of the specified object, and
then shifts up objects at and below the deletion point.
-
void removeElementAt(int index)
-
Deletes the object at the specified index position, and then
shifts up objects at and below the deletion point.
-
void setElementAt (Object obj, int index)
-
Replaces the existing object at the specified index position with
the specified object.
-
int size ()
-
Returns the number of objects currently stored in the vector.
|
That's only part of what a Vector does best!
|
To create a new Vector
|
... one should invoke a no-arg constructor.
|
Something like this:
Vector a = new Vector();
|
This creates a new (and empty) Vector object.
|
Can you define a class Vector ?
|
Here's a reasonable start:
|
class Vector {
Object[] localStorage = new Object[0];
int size() {
return localStorage.length;
}
void addElement(Object obj) {
int currentLength = this.size();
Object[] aux = new Object[currentLength + 1];
aux[currentLength] = obj;
for (int i = 0; i < currentLength; i++) {
aux[i] = localStorage[i]; // transfer elements
}
localStorage = aux;
}
public String toString() {
String returnString = "Vector of size " + size() + ": (";
for (int i = 0; i < localStorage.length; i++)
returnString += " " + localStorage[i]; // delay printing...
return returnString + ")";
}
public static void main(String[] args) {
Vector v = new Vector();
System.out.println(v); // convenient printing
v.addElement(new Integer(2));
System.out.println(v);
v.addElement(new Integer(4));
System.out.println(v);
v.addElement(new Integer(6));
System.out.println(v);
}
}
What would this be?
|
An operational model for java.util.Vector
|
"What I cannot create, I cannot understand."
|
So we put one together, just for the fun of it.
|
Very good. Can I see this running?
|
No.
|
What do you mean?
|
Of course you can!
|
tucotuco.cs.indiana.edu% javac Vector.java
tucotuco.cs.indiana.edu% java Vector
Vector of size 0: ()
Vector of size 1: ( 2)
Vector of size 2: ( 2 4)
Vector of size 3: ( 2 4 6)
tucotuco.cs.indiana.edu%
Now let's look at the code line by line.
|
It will be my pleasure.
|
class Vector {
Object[] localStorage = new Object[0];
I see you use an array of Object s inside an instance of my Vector class.
|
OK, I should have made it private , but now it's too late.
|
This is the internal (local) storage that is available to every
instance of type Vector ,
|
... to every object that is instantiated
with the new operator out of class Vector .
|
I make a distinction between an array of size 0 and no
array whatsoever (that is, a null pointer).
|
And so do I.
|
I start with an array of size 0 because I want my
localStorage to always have a useful length
to return (a null pointer returns an exception when asked
about it's length .)
|
That was exactly my thinking.
|
So I start with an array of size 0 for uniformity.
|
And implementing size is a breeze.
|
int size() {
return localStorage.length;
}
Now let's tell the story of addElement .
|
|
void addElement(Object obj) {
This method will reveal our style of implementation.
|
I will not worry about efficiency, I will rely heavily on Java's automatic memory management facilities.
|
It's good to optimize one's programs.
|
Yes, but as Donald Knuth remarked once, "premature optimization is the root of all evil".
|
Once my program works fine I can try to optimize it.
|
But first, I need to make it work correctly.
|
Correctness concerns us most at this stage.
|
Yes. Let's start. We first get the size of the vector (our local storage is always
at capacity.)
|
int currentLength = this.size();
Would've worked just as well if we had talked to localStorage directly,
right?
|
Yes, but this way we are expressing ourselves in Vector terms.
|
Then we get ready to store a new element,
|
... and allocate space that's bigger with one than our current size.
|
Object[] aux = new Object[currentLength + 1];
Now place the object that needs to be added in the last position.
|
Indeed, that's what we said we'd be doing.
|
aux[currentLength] = obj;
Transfer the objects from the local storage into the new array one by one.
|
|
for (int i = 0; i < currentLength; i++) {
aux[i] = localStorage[i];
}
|
Then call this new array our localStorage .
|
localStorage = aux;
What will happen to the array that the localStorage
variable's previously pointing to?
|
Gone with the wind.
|
That's quite correct. And since no variable references it any longer,
|
... it will be collected by Java's garbage collector.
|
}
Curly brace.
|
End of story.
|
That's how we add a new element to a Vector .
|
Removing an element should be similar.
|
The rest of my program only serves testing purposes.
|
Yes, but it's still instructive!
|
If I ever place a Vector in a String context
|
... it will be this method that will kick in.
|
public String toString() {
Let's see how it works. |
I start by printing the type (vector) and the size. |
String returnString = "Vector of size " + size() + ": (";
You're not really printing, are you?
|
No, just putting together a String representation
that will eventually be printed (but not here).
|
The elements if any will be listed one by one
|
|
for (int i = 0; i < localStorage.length; i++)
returnString += " " + localStorage[i];
|
... and the list will appear in between parens.
|
return returnString + ")";
}
The main method creates a vector, and adds three elements to it.
|
We could have used Product s, you know?
|
Yes, but this points out what you need to do if you want to store numbers.
|
You have to use wrapper classes.
|
Which are number holders.
|
... and after each addition the Vector is printed.
|
public static void main(String[] args) {
Vector v = new Vector();
System.out.println(v);
v.addElement(new Integer(2));
System.out.println(v);
v.addElement(new Integer(4));
System.out.println(v);
v.addElement(new Integer(6));
System.out.println(v);
}
}
Well, that was easy!
|
Easy and clear!
|
What's a Hashtable ?
|
Let me look it up!
|
http://java.sun.com/products/jdk/1.2/docs/api/java/util/Hashtable.html
A Hashtable is a generalized kind of Vector .
|
A Vector is a generalized kind of array.
|
The Vector is more general than an array because it grows
and shrinks dynamically, and the elements don't have to be of the same type.
|
Both arrays and Vector s map indices (numbers) to elements contained.
|
They're sequences.
|
A Hashtable is more general in that it stores associations between
keys and values.
|
The keys and the values could be any objects.
|
For example String s.
|
If you wanted to store associations of this kind:
username --> password
using Vector s or arrays
|
You'd have to use parallel Vector s or arrays!
|
Hashtable s are built just for that!
|
They're sometimes called associative arrays.
|
Once the association is built,
|
... we want to use it.
|
So, given a username , we need to...
|
... look up the password that is associated with it.
|
That's what Hashtable s do best!
|
Here's an example
that uses a Hashtable
object to interactively manage a
set of String s.
|
import java.util.*;
class Set {
public static void main(String[] args) {
Hashtable h = new Hashtable();
ConsoleReader b = new ConsoleReader(System.in);
System.out.println("Ready for input.");
while (true) {
StringTokenizer s = new StringTokenizer(b.readLine());
String command = "", argument = "";
if (s.hasMoreTokens()) { command = s.nextToken(); }
if (s.hasMoreTokens()) { argument = s.nextToken(); }
if ( command.equals("add")) {
h.put(argument, argument);
System.out.println("OK");
} else if (command.equals("delete")) {
h.remove(argument);
System.out.println("OK");
} else if (command.equals("list")) {
Enumeration e = h.keys();
while (e.hasMoreElements()) {
System.out.println(" :" + e.nextElement());
}
} else if (command.equals("contains")) {
System.out.println(h.containsKey(argument));
} else if (command.equals("quit")) {
System.exit(0);
} else {
System.out.println("Unknown command: " +
command + " " + argument);
}
}
}
}
This is an interactive program.
|
Yes, you need to type commands into it.
|
Can I see it running?
|
Here's the program (in blue) talking to a user.
|
frilled.cs.indiana.edu%java Set
Ready for input.
add one
OK
add two
OK
list
:one
:two
add three
OK
list
:one
:two
:three
add three
OK
list
:one
:two
:three
delete three
OK
list
:one
:two
contains three
false
contains two
true
quit
frilled.cs.indiana.edu%
Arrays and vectors can store linear sequences.
|
Hashtables are functions of just one variable.
|
It often happens that you want to store collections
that have a two-dimensional layout.
|
Such an arrangement, consists of rows and columns
of values, and is called (not surprisingly) a
two-dimensional array.
|
... or matrix (sometimes).
|
When constructing a two-dimensional array, you specify
how many rows and columns you need.
|
If you want 10 rows and 8 columns:
|
|
int[][] matrix = new int[10][8];
To access a particular element in the array
|
|
matrix[3][4] = 5;
|
... specify two subscripts, in separate brackets.
|
In Java, two-dimensional arrays are stored as arrays of arrays.
|
And three dimensional arrays are stored as arrays of two-dimensional arrays.
|
That means arrays of arrays of arrays.
|
Yes, arrays of (arrays of arrays).
|
And n-dimensional arrays will be stored as
arrays of (n-1)-dimensional arrays.
|
What a recursive definition.
|
We won't use more than two-dimensions.
|
Let's take a closer look at them though: because a two-dimensional array is really an array of the row arrays,
|
... the number of rows is:
|
And the number or columns?
|
int nrows = matrix.length;
Because a two-dimensional array is really an array of the row arrays,
|
... the number of elements in each row can vary.
|
|
If it doesn't, the number of columns is the same as the length of the first row:
|
int ncols = matrix[0].length;
Can I see an example of a two-dimensional array that has rows of various lengths?
|
Yes, you can quickly create one with an array initializer:
|
int[][] b = { {1},
{2, 3},
{4, 5, 6},
{7, 8, 9, 10}
};
Can you do the same thing without it?
|
Sure, I thought you'd ask:
|
int[][] b = new int[4][];
int count = 1;
for (int i = 0; i < 4; i++) {
b[i] = new int[i + 1];
for (int j = 0; j <= i; j++) {
b[i][j] = count;
count += 1;
}
}
I see you have to work harder.
|
Yes, first you allocate space to hold four rows.
|
You indicate that you will manually set each row by leaving
the second array index empty.
|
Then you need to allocate (and fill) each row separately.
|
You can access each array element as b[i][j] ,
|
... but you must be careful that j is less than
b[i].length as illustrated below:
|
class Testing {
public static void main(String[] args) {
int[][] b = new int[4][];
int count = 1;
for (int i = 0; i < 4; i++) {
b[i] = new int[i + 1];
for (int j = 0; j <= i; j++) {
b[i][j] = count;
count += 1;
}
}
for (int i = 0; i < b.length; i++) {
for (int j = 0; j < b[i].length; j++) {
System.out.print(b[i][j] + " ");
}
System.out.println();
}
}
}
Naturally, such "ragged" arrays are not very common.
|
Except when you need them.
|
Then they become very natural.
|
And they're no longer "ragged".
|
Exactly, because they fit that problem
|
... perfectly.
|
Last updated: May 16, 2004 by Adrian German for A201