|
CSCI A201/A597 and I210
Lecture Notes Twenty-One
Second semester 2000-2001
|
Vectors, hasthables, leftovers.
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);
}
}
|
Can I see this running?
|
No.
|
|
What do you mean?
|
Of course!
|
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: Mar 8, 2001 by Adrian German for A201