using java
by Prithvi Rao
<prithvi+@ux4.sp.cs.cmu.edu>
Prithvi Rao is the co-founder of KiwiLabs, which specializes in
software engineering methodology and Java/CORBA training. He has also
worked on the development of the MACH OS and a real-time version of
MACH. He is an adjunct faculty at Carnegie Mellon and teaches in the
Heinz School of Public Policy and Management.
The Java platform provides the programmer with a core set of classes
that are in "packages," and we have seen many examples (such as java.io
and java.net) in previous articles.
The way data is stored and structured in your program can make the
difference between a clean, elegant solution and a mass of
unrecognizable tangled code. For example, we know that an instance
variable of a class can take the place of data structures in other
languages; also, an array can hold many copies of one type of data.
Nevertheless, we sometimes require "data containers" that are more
powerful and flexible than either of these options.
Java provides a set of "collection classes" as part of the java.util
package that are ready-made solutions to common data-storage problems.
We will examine a few of the collection classes in Java. I'll leave it
to the reader to extrapolate the use of the remaining collections by
understanding the ones presented here.
Specifically we present the Stack class, Abstract Dictionaries,
Property Lists, and Random Numbers.
Collections Explained
Collections in Java are not type-specific and can hold any type of
object. This makes a collection a very flexible storage medium. When an
object is stored in a collection it is implicitly cast to an Object.
(Recall that Object is the base class from which all other classes are
derived.) The methods used to store and retrieve Objects are
collection-specific. For purposes of consistency, an object retrieved
from a collection is returned as an Object. In order to perform a
useful action on the Object, it must be recast to a reference of its
original type (or its superclass).
The flexibility of Java collections can also be a disadvantage. For
instance, since the collections are not type-specific, there is no type
checking when an object is added. Consequently a collection of Strings
can have a Thread object added to it without the compiler of Java
runtime catching it.
The correct programming paradigm for code recovering objects from
collections is always to catch ClassCastException.
The Stack Class
The Stack class implements a last-in-first-out stack of Objects.
Objects can be "pushed" onto the top of the stack, then "popped" off.
The first object to be pushed onto the stack will be the last one to be
popped, and vice versa. To facilitate these operations, the methods
push and pop are provided.
Popping an object off the stack will remove it from the stack. If you
wish to examine an object on the top without removing it, then you can
use the method peek().
In general, in order to examine the contents of the stack, you can use
the search() method. This will look at the elements of the stack for
the Object supplied as a parameter using the Object.equals() method as
a means of locating it. If it finds it, the search returns its position
in the stack.
If the return value is 1, this means that the Object is the topmost
item in the stack. A value of 2 suggests the second place, and so on. A
value of -1 suggests that the Object is not on the stack.
Both pop() and peek() will generate an EmptyStackExpression if there is
no Object on the stack. You should always perform a boolean check with
the empty() method when popping Objects from the stack.
Abstract Dictionaries
The Dictionary class is an abstract class that provides methods to
store and retrieve an Object indexed by a key rather than an index. All
of its methods are abstract. (It is effectively an interface, being
"extended" rather than "implemented.") This means that the precise
algorithms used to map and store keys are determined within the
subclasses.
The simplest manipulations are put() and get(). The put() method places
the Object into the Dictionary in an appropriate place based on the key
that has been supplied. If another Object has already been stored using
the same key, the new Object is stored with that key, and the put()
method returns the Object previously stored there. The get() method
returns the Object stored under the key supplied. In both cases, get()
and put(), if the position associated with the key supplied is not
occupied, a value of null is returned.
This means that neither the key nor the element can be null, since null
is used to indicate that an Object was not found in that location.
Attempts to use null will result in an exception.
Objects can be removed from the Dictionary with remove(), which returns
the object associated with the given key and removes it. The current
number of Objects in the Dictionary can be determined with size(); the
is Empty() method is a special case to test for a size of 0 (zero).
We know that abstract classes are not in themselves very useful, but
the Dictionary class is the parent class of two other very useful
classes: HashTable and Properties.
Properties Lists
The Properties class builds on HashTable to provide an interface to a
set of key/value pairs, both of which are strings. The most obvious
place this is used is for the list of system properties with
System.getProperty(). Although the method names used to manipulate the
property list are specific to the Properties class, they all use the
HashTable methods to store and retrieve data.
The no-argument constructor creates an empty property list. You can use
put() to populate this list. The alternative constructor takes a
default Properties list. If the requested key is not found in the
current Properties object, then it is searched for in the default list.
Note that the default list can itself have a default list, etc. It is
not possible to change the default property list after construction.
The simplest thing to do with a Properties object is to query it with
getProperty(). You supply the key, and it returns the matching value
String (or null if not found). The alternative form allows you to
supply a default value to be returned instead of null if the key is not
found.
The Properties class also has a useful feature that allows you to save
a properties list to an output stream and recover it from an input
stream (typically a file). The save() method writes the list to the
given stream. It is acceptable to supply a single-line comment that is
placed at the start of the file. These properties can subsequently be
read into a Properties object with load().
All of the properties can be recovered as an enumeration with the
propertyNames() method or listed on a given print stream with list().
Random Numbers
The Random class permits the user to create and manipulate a
pseudo-random-number generator. The argument constructor provides a
random-number generator seeded with a value based on the current time.
Alternatively, it is possible to set your own "long" seed value in the
constructor or later with setSeed().
Once you have your Random object, you can generate uniformly
distributed pseudo-random numbers of various types: int, long, float,
double. In each case, the value generated will be between the minimum
and maximum values for the given type, for example Integer.MIN_VALUE or
Integer.MAX_VALUE.
If you do not wish to deal with all the overhead of object creation and
destruction, it is possible to use the static method random() in the
Math class. This returns a double value that can be converted into the
desired type.
Example Using the Properties Collection Class
This program displays the system properties specified as input
parameters or, if no input is given, all the system properties.
package java4cpp.collections;
import java.util.*;
public class SystemProperties {
public static void main(String[] args) {
if (args.length > 0) {
for (int i = 0; i < args.length; i++) {
System.out.println(args[i] + ": " +
System.getProperty(args[i], "not found"));
}
} else {
// dump all the system properties
Properties sysProps = System.getProperties();
Enumeration e = sysProps.propertyNames();
while(e.hasMoreElements() ) {
String propName = (String) e.nextElement();
System.out.println(propName + ": " +
sysProps.getProperty(propName));
}
}
}
}
Compile the program: javac SystemProperties.java
Run the program: java SystemProperties
When there are no input arguments, the output describes the native
environment on which the program has run and the implementation of the
JVM used. The following output or one similar will be displayed if you
run this program.
user.language: en
user.name: prithvi
java.home: /usr/local/jdk1.2.2/bin
file.encoding.pkg: sun.io
java.version: 1.2.2
file separator: /
line.separator:
user.region: US
file.encoding: 8859_1
user.timezone: EST
path.separator: :
Summary
We have examined some of the collections that are part of Java.
Collections can be organized into abstract structures such as sets,
bags, lists, stacks, trees, queues, hash tables, and many others. When
selecting a collection class, we have seen that there are abstract
classes that must be extended or that simply use one of the data
structures already encapsulated by any of the core collection class
(such as Properties in the example above). No matter which you choose,
there is a greater chance that your code will be more elegant.
The overhead of object creation and destruction can result in a
possible performance degradation, but if used wisely this too can be
ameliorated.
|