Lecture Notes Twenty-One: Vectors, Hashtables, Leftovers.
First here's a Selection Sort we will develop 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.
We will talk about inheritance.
The basic part we need to know is this example:
class Horse {
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();
Horse b = new Unicorn();
Unicorn c = new Unicorn();
// Unicorn d = new Horse();
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?
}
}
Here's a picture to get the idea:
That's what you need to know to understand applets.
It's also important to understand how Vectors work.
We will talk about Vectors in a little while.
Here's bit of help with Lab 11's Problem 7:
public static int count(int[] a, int value) {
int sum = 0;
for (int i = 0; i < a.length; i++)
if (a[i] == value)
sum += 1;
return sum;
}
This is 11.3.2 in the book (counting occurrences in an array).
Also described in lecture notes eighteen.
Here's the other bit of help, with Lab 11's Problem 6:
public static boolean contains(int[] a, int value) {
return count(a, value) > 0;
}
This is 11.3.1 in the book (searching for a value in an array).
This version relies on the method defined above.
Now let's talk about Vectors.
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 = size();
Object[] aux = new Object[currentLength + 1];
aux[currentLength] = obj;
for (int i = 0; i < currentLength; i++) {
aux[i] = localStorage[i];
}
localStorage = aux;
}
public String toString() {
String returnString = "Vector of size " + size() + ": (";
for (int i = 0; i < localStorage.length; i++)
returnString += " " + localStorage[i];
return returnString + ")";
}
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);
}
}
|
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 Objects 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.
|
|
So correctness is what 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 = size();
|
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 was previously pointing to?
|
Gone with the wind.
|
|
That's right, 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
|
... the representation returned by this method will appear.
|
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 Products, 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 Vectors 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 Strings.
|
Indeed, if you want to store all the associations of this kind:
username --> password
using Vectors or arrays
|
You will have to use parallel Vectors or arrays!
|
|
Once the association is built,
|
... we want to use it.
|
So, given a username
|
... we need to look the password that is associated with it.
|
A Hashtable is made for that.
|
Here's an example (below) that uses a Hashtable object to interactively manage a set of Strings.
|
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.
|
Likewise, 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, consisting of rows and columns
of values is called a two-dimensional array
|
|
... or matrix.
|
When constructing a two-dimensional array, you specify
how many rows and columns you need.
|
If you want 10 rows and 8 columns:
|
A 10 x 8 matrix of ints.
|
int[][] matrix = new int[10][8];
|
To access a particular element in the array
|
... specify two subscripts, in separate brackets.
|
matrix[3][4] = 5;
|
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.
|
Yes, and the lab today will have an interesting application of two-dimensional arrays.
|
|
Very good.
|
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: Jul 23, 2001 by Adrian German for A201