All Categories :
Java
Chapter 13
The Utilities Package
by Michael Girdley and Richard Lesh
CONTENTS
This chapter describes the classes in the java.util package
in the Java class library. These classes implement many of those
features or functions usually left for the programmer or someone
else to implement. In programming experiences, I regularly find
myself saying, "It would be so much easier if there were
a built-in object that would do some common but complicated
task." The java.util package is a well-designed
and effective attempt to satisfy many of these specialized needs.
In many languages, you find yourself implementing a stack or a
hash table class and all the corresponding methods. Java has a
built-in stack type that enables you to quickly and efficiently
include your own stack data structures in your Java programs.
This frees you to deal with more important design and implementation
issues. These classes are also useful in a variety of other ways
and are the fundamental building blocks of the more complicated
data structures used in other Java packages and in your own applications.
This chapter covers the following topics:
- Each of the features of the utilities package
- The implementation of each of the different classes in the
utilities package
NOTE |
Unless otherwise noted, all the interfaces and classes discussed in this chapter extend the java.lang.Object class.
|
Table 13.1 shows the classes that are part of the utilities package
and that are discussed in this chapter.
Table 13.1. Utilities package classes.
Class | Description
|
BitSet | Implements a collection of binary values
|
Date | Used for date and time data storage and use
|
Dictionary | Used to store a collection of key and value pairs
|
Hashtable | Used to store a hash table
|
Observable | Used to store observable data
|
Properties | Used to store and make use of a properties list that can be saved
|
Random | Used to generate a pseudo-random number
|
Stack | Used to store and implement a stack
|
StringTokenizer | Used to tokenize a string
|
Vector | Used to store a vector data type
|
NOTE |
You may not be familiar with some of these data types. The Dictionary class is used to implement a dictionary in your program. A Hashtable is a storage data type whose speed in searching is much greater than that of other data structures because it stores data items based on a key derived from some given formula. A Stack, of course, functions as if you were stacking data items on the floor, one on top of the other, in a single stack. As a consequence, the only two manipulations you can make to the stack are to remove the top item or to place another item on top. The Vector class implements an interesting data structure that has the capability to begin with a limited capacity and then change in size to accommodate the data items you insert into it. The Vector class can be described as a "growable array."
|
One would expect that the Vector class would eliminate
the necessity for creating your own data structures. But there
may be times when you want to conserve space to the maximum or
access your data in a specialized way. In these cases, there is
a technique to implement such data structures in Java.
As you know, Java has no pointers. Because dynamically linked
lists and queues are implemented using pointers, is it then impossible
to create these two data structures in Java? Not quite. Just as
with many other tasks in Java, you have to do a little "funky
stepping" to get it right because the implementation of lists,
queues, and other dynamic data structures is not intuitive.
To define your own dynamic data structures, you will want to make
use of the fact that references to objects in Java are
already dynamic. This is demonstrated and necessitated by the
practices Java uses, such as interfaces and abstract implementations.
If you are accustomed to implementing dynamically linked lists
or queues in C++, the format you use in Java to create your own
version of these structures should seem very familiar to you.
For example, the following code creates a Node class
for the list that contains a string:
class Node {
String Name;
Node Prev;
Node Next;
}
Of course, this code creates a doubly linked list, which
has links backward and forward to other nodes containing strings.
You could easily convert this type to link objects in just about
any way to exhibit just about any behavior you want: queues, stacks
(remember, there is already a Stack object in the class
library), doubly linked lists, circular lists, binary search trees,
and so on.
To implement such a list, you create a DoubleList class
that contains one such Node object and links strung out
from there. You can use the keyword null to represent
an empty object. Here is an example of the DoubleList
declaration:
class DoubleList {
// Declare the listhead to be of the Node type we created earlier.
// Also, set it to be an empty object.
Node ListHead = null;
.
.
}
You then create methods to act on the list, such as InsertNode()
or ClearMyListJerk()--whatever you want.
You may also want to create a constructor method for the Node
class that accepts parameters to set the previous and next nodes
at construction time; or you may want to create a method such
as SetNext() or SetNextToNull(). Either choice
would work just fine.
NOTE |
Out of all this you get a surprise bonus: No worry about freeing space allocated to create nodes because the Java garbage collection processes take care of all that for you. Just create objects when you need them and let Java take care of it.
|
The utilities package has two interfaces you can use in classes
of your own design: Enumeration and Observer.
An interface is a set of methods that must be written for any
class that claims to implement the interface. This arrangement
provides a way to consistently use all classes that implement
the interface. Following is a summary of the Enumeration
and Observer interfaces:
Enumeration
|
Interface for classes that can enumerate a vector
|
Observer
|
Interface for classes that can observe observable objects
|
The Enumeration interface is used for classes that can
retrieve data from a list, element by element. For example, there
is an Enumeration class in the utilities package that
implements the Enumeration interface for use with the
Vector class. This frees you from hard-core traversal
of the different classes of data structures.
The Observer interface is useful in designing classes
that can watch for changes that occur in other classes.
CAUTION |
Some of the examples in this chapter are not applets--they are applications. Many of these data structures are best exhibited by just plain text input and output. Removing the baggage that would have come along with applets allows the examples to be simplified so that the topic being demonstrated is clearer.
When you apply any code segments from this chapter in your own applets, remember that some of the examples are not true applets; you must deal with the differences inherent between applets and applications.
|
The Enumeration interface specifies a set of methods
used to enumerate--that is, iterate through--a list. An object
that implements this interface can be used to iterate through
a list only once because the Enumeration object is consumed
through its use.
For example, an Enumeration object can be used to print
all the elements of a Vector object, v, as follows:
for (Enumeration e=v.elements();e.hasMoreElements();)
System.out.print(e.nextElement()+" ");
The Enumeration interface specifies only two methods:
hasMoreElements() and nextElement(). The hasMoreElements()
method must return true if there are elements remaining
in the enumeration. The nextElement() method must return
an object representing the next element within the object that
is being enumerated. The details of how the Enumeration
interface is implemented and how the data is represented internally
are left up to the implementation of the specific class.
The Observer interface, if implemented by a class, allows
an object of the class to observe other objects of the class Observable.
The Observer interface is notified whenever the Observable
object that it is watching changes.
The interface specifies only one method, update(Observable,
Object). This method is called by the observed object to
notify the Observer of changes. A reference to the observed
object is passed along with any additional object that the observed
object wants to pass to the Observer. The first argument
enables the Observer to operate on the observed object;
the second argument is used to pass information from the observed
to the Observer.
The utilities package supplies 10 different classes that provide
a wide variety of functionality. Although these classes don't
generally have much in common, they all provide support for the
most common data structures used by programmers. The techniques
described in the next sections enable you to create your own specialized
classes to supplement those missing from the package.
The classes supplied in the java.util package, however
limited they are, do provide a great advantage over other languages.
The main advantage is that these classes simplify some things
and eliminate a lot of the garbage you were stuck with in the
past, in terms of freeing memory and doing mundane programming
tasks.
However, there are a number of limitations. For example, you have
to "dance a little bit" to implement some of the more
complicated data structures. And if you want speed, there are
much faster languages to choose from. Java provides a combination
of power and simplicity while sacrificing speed. However, don't
worry that your programs will be slugs. Although Java is not nearly
as efficient as C++ and C, it still beats Visual Basic in terms
of size and speed.
The BitSet class implements a data type that represents
a collection of bits. The collection grows dynamically as more
bits are required. The class is useful for representing a set
of true and false values. Specific bits are
identified using nonnegative integers. The first bit is bit 0.
The BitSet class is most useful for storing a group of
related true/false values, such as user responses to
Yes and No questions. For example, if the applet has a number
of radio buttons, you can slap those values into an instance of
the BitSet class.
The class is also useful in terms of bitmapping your own graphics.
You can create bitsets that represent a pixel at a time (of course,
it would be much easier to use the Graphics class for
this purpose instead).
Individual bits in the set are turned on or off with the set()
and clear()methods. Individual bits are queried with
the get() method. These methods all take the specific
bit number as their only argument. The basic boolean operations
AND, OR, and XOR can be performed on
two bitsets using the and(), or(), and xor()
methods. Because these methods modify one of the bitsets, you
generally use the clone() method to create a duplicate
of one bitset, and then use the AND, OR, or
XOR operation on the clone with the second bitset. The
result of the operation then ends up in the cloned bitset. The
BitSet1 program in Listing 13.1 shows the basic BitSet
operations.
Listing 13.1. BitSet1.java: a sample BitSet
program.
import java.io.DataInputStream;
import java.util.BitSet;
class BitSet1 {
public static void main(String args[])
throws java.io.IOException
{
DataInputStream dis=new DataInputStream(System.in);
String bitstring;
BitSet set1,set2,set3;
set1=new BitSet();
set2=new BitSet();
// Get the first bit sequence and store it
System.out.println("Bit sequence #1:");
bitstring=dis.readLine();
for (short i=0;i<bitstring.length();i++){
if (bitstring.charAt(i)=='1')
set1.set(i);
else
set1.clear(i);
}
// Get the second bit sequence and store it
System.out.println("Bit sequence #2:");
bitstring=dis.readLine();
for (short i=0;i<bitstring.length();i++){
if (bitstring.charAt(i)=='1')
set2.set(i);
else
set2.clear(i);
}
System.out.println("BitSet #1: "+set1);
System.out.println("BitSet #2: "+set2);
// Test the AND operation
set3=(BitSet)set1.clone();
set3.and(set2);
System.out.println("set1 AND set2: "+set3);
// Test the OR operation
set3=(BitSet)set1.clone();
set3.or(set2);
System.out.println("set1 OR set2: "+set3);
// Test the XOR operation
set3=(BitSet)set1.clone();
set3.xor(set2);
System.out.println("set1 XOR set2: "+set3);
}
}
The output from this program looks like this:
Bit sequence #1:
1010
Bit sequence #2:
1100
BitSet #1: {0, 2}
BitSet #2: {0, 1}
set1 AND set2: {0}
set1 OR set2: {0, 1, 2}
set1 XOR set2: {1, 2}
Table 13.2 summarizes all the methods available in the BitSet
class.
Table 13.2. Methods in the BitSet interface.
Method | Description
|
Constructors
|
BitSet() | Constructs an empty BitSet
|
BitSet(int) | Constructs an empty BitSet of a given size
|
Methods |
and(BitSet) | Logically ANDs the object's bitset with another BitSet object
|
clear(int) | Clears a specific bit
|
clone() | Creates a clone of the BitSet object
|
equals(Object) | Compares this object against another BitSet object
|
get(int) | Returns the value of a specific bit
|
hashCode() | Returns the hash code
|
or(BitSet) | Logically ORs the object's bitset with another BitSet object
|
set(int) | Sets a specific bit
|
size() | Returns the size of the set
|
toString() | Converts bit values to a string representation
|
xor(BitSet) | Logically XORs the object's bitset with another BitSet object
|
In addition to extending the java.lang.Object class,
BitSet implements the java.lang.Cloneable interface.
This, of course, allows instances of the object to be cloned to
create another instance of the class.
You will regularly run into instances in which you have to access
and manipulate dates and times in your applets on the Web. For
example, you may want an applet to display the current time or
date during its execution. If you are programming a game, you
may want to use the system clock to get your elapsed time right.
The Date class is used to represent dates and times in
a platform-independent fashion. For example, the current date
or a specific date can be printed as shown in Listing 13.2.
Listing 13.2. Date1.java: A sample Date
program.
import java.util.Date;
public class Date1{
public static void main (String args[]){
Date today=new Date();
System.out.println("Today is "+today.toLocaleString()+
" ("+today.toGMTString()+")");
Date birthday=new Date(89,10,14,8,30,00);
System.out.println("My birthday is"+
birthday.toString()+" ("+birthday.toGMTString()+")");
Date anniversary=new Date("Jun 21, 1986");
System.out.println("My anniversary is "+
anniversary+" ("+anniversary.toGMTString()+")");
}
}
The output from this program looks like this:
Today is 01/21/97 19:55:17 (22 Jan 1997 01:55:17 GMT)
My birthday is Thu Nov 14 08:30:00 1989 (14 Nov 1989 14:30:00 GMT)
My anniversary is Sat Jun 21 00:00:00 1989 (21 Jun 1986 05:00:00 GMT)
The default constructor is used when the current date and time
are needed. A specific date and time can be used to initialize
a Date object using the constructors that take three,
five, and six integers. These constructors allow the date and
time to be specified using YMD, YMDHM, or YMDHMS formats. Any
parts of the time not specified by the three-integer and five-integer
constructors are set to zero.
NOTE |
Date and time formats can be conveniently summarized using notations of the form YMD, YMDHMS, HMS, or MDY. These abbreviated formats indicate the order in which the various numeric parts of the date appear. Each letter refers to a specific component of the date or time: year (Y), month (M), day (D), hour (H), minute (M), and second (S). Whether the letter M refers to month or minute depends on the context
|
Alternatively, a Date object can be constructed using
a single string that represents a date and time using a variety
of different syntax. One of the most important is the international
standard date syntax of the form, "Sun, 14 Aug 1995 9:00:00
GMT." Continental U.S. time zone abbreviations are understood,
but time zone offsets should be considered for general use; for
example, "Sun, 14 Aug 1995 9:00:00 GMT+0600" (six hours
west of the Greenwich meridian). The local time zone to the computer
executing the code is assumed if none is supplied.
NOTE |
The Date class intends to store date and time information in UTC (Coordinated Universal Time). However, the class does not necessarily achieve this goal. UTC is a time standard based on an atomic clock. Time specifications using UTC are considered equal to GMT (Greenwich Mean Time). The implementation of the Date class is limited by the time set by the underlying operating system. Because modern operating systems typically assume that a day is always 86,400 seconds, the extra leap seconds, which are needed about once a year to accurately reflect UTC, are usually not added.
|
The date can be converted to a text representation using the methods
toString(), toGMTString(), and toLocaleString(),
which convert the date and time to the standard UNIX, GMT, or
local time formats. The toLocaleString() function is
very useful because you do not have to determine what your system's
date format is. This may not sound like much, but it is just another
piece of the very complicated puzzle that Sun has put together
to allow your applets and applications to flow seamlessly into
the system on which they are running.
When a date is converted to a string by an automatic coercion,
the toString() method is used. The resulting string returned
by the toString() function follows UNIX time and date
standards.
The Date class also has methods for setting and querying
the date and time component values once the Date object
is constructed. The individual parts of the date (month, date,
year) and time (hours, minutes, seconds) are always specified
in local time. When referring to the various parts of the date
and time, the first letter of each part is typically used in an
abbreviation. For example, YMDHMS indicates that all six parts
(year, month, date, hour, minute, second) are present. Each of
these parts of the date and time have a specific range of acceptable
values, as shown in Table 13.3.
Table 13.3. Date component ranges.
Component | Range
|
Year | Year minus 1900
|
Month | 0 to 11 (January=0)
|
Date | 1 to 31
|
Day | 0 to 6 (Sunday=0)
|
Hour | 0 to 23
|
Minute | 0 to 59
|
Second | 0 to 59
|
The date and time also can be specified using a single integer
UTC value that represents the number of milliseconds that have
elapsed since a specific starting date (which may vary from system
to system). For UNIX systems, this date is January 1, 1970. The
program Date2 in Listing 13.3 shows how this single value
corresponds to the normal YMDHMS representation.
Listing 13.3. Date2.java: A sample Date
program.
import java.util.Date;
public class Date2{
public static void main (String args[]){
Date beginning=new Date(0);
Date anniversary=new Date("Jun 21, 1986");
Date today=new Date();
System.out.println(beginning+"="+beginning.getTime());
System.out.println(anniversary+"="+anniversary.getTime());
System.out.println(today+"="+today.getTime());
}
}
The output from this program looks like this:
Wed Dec 31 18:00:00 1969=0
Sat Jun 21 00:00:00 1986=519714000000
Sun Jan 21 19:55:17 1996=822275717000
Dates can be compared to each other by using this UTC value or
by using the method after(), before(), or equals().
CAUTION |
Don't try to launch space shuttles or coordinate nuclear attacks based on your operating system's local time as reflected by Java. Although the API is intended to reflect UTC (Coordinated Universal Time), it doesn't do so exactly. This inexact behavior is inherited from the time system of the underlying OS. The vast majority of all modern operating systems assume that one day equals 3600 seconds/hour times 24 hours, and as such, they reflect time to the accuracy that UTC does.
Under the UTC, about once a year, there is an extra second, called a "leap second," added to account for the wobble of the earth. Most computer clocks are not accurate enough to reflect this distinction.
Between UTC and standard OS time (UT/GMT), there is this subtle difference; one is based on an atomic clock, and the other is based on astronomical observations--which, for all practical purposes, is an invisibly fine hair to split.
For more information, Sun suggests you visit the U.S. Naval Observatory site, particularly the Directorate of Time at http://tycho.usno.navy.mil and its definitions of different systems of time at http://tycho.usno.navy.mil/systime.html.
|
Table 13.4 summarizes all the methods available in the Date
class.
Table 13.4. Methods available in the Date
interface.
Method | Description
|
Constructors
|
Date() | Constructs a date using today's date and time
|
Date(long) | Constructs a date using a single UTC value
|
Date(int, int, int) | Constructs a date using YMD format
|
Date(int, int, int, int, int) |
Constructs a date using YMDHM format |
Date(int, int, int, int, int, int)
| Constructs a date using YMDHMS format |
Date(string) | Constructs a date from a string
|
Static Methods
|
UTC(int, int, int, int, int, int) |
Calculates a UTC value from YMDHMS format |
parse(string) | Returns the single UTC value of a date in text format
|
Methods |
after(Date) | True if the date is later than the specified date
|
before(Date) | True if the date is earlier than the specified date
|
equals(Object) | True if the date and the specified date are equal
|
getDate() | Returns the day of the month
|
getDay() | Returns the day of the week
|
getHours() | Returns the hour
|
getMinutes() | Returns the minute
|
getMonth() | Returns the month
|
getSeconds() | Returns the second
|
getTime() | Returns the time as a single UTC value
|
getTimezoneOffset() | Returns the time zone offset, in minutes, for this locale
|
getYear() | Returns the year after 1900
|
hashCode() | Computes a hash code for the date
|
setDate(int) | Sets the date
|
setHours(int) | Sets the hours
|
setMinutes(int) | Sets the minutes
|
setMonth(int) | Sets the month
|
setSeconds(int) | Sets the seconds
|
setTime(long) | Sets the time using a single UTC value
|
setYear(int) | Sets the year
|
toGMTString() | Converts a date to text using Internet GMT conventions
|
toLocaleString() | Converts a date to text using locale conventions
|
toString() | Converts a date to text using UNIX ctime() conventions
|
One of the most helpful methods available in the Date
class is the parse() method. This void takes
an instance of the String type and then parses that string.
The result of that parse() is then placed in the calling
instance of the class. If you have a date called ADate,
you can set its value to be the date in the SomeString
class with this code line:
ADate.parse(SomeString);
You will also find the before() and after()
functions useful. They enable you to send in another instance
of the Date class and compare that date to the value
in the calling instance.
The sample applet in Listing 13.4 demonstrates the use of the
Date class.
Listing 13.4. Using the Date class.
import java.awt.*;
import java.util.*;
public class MichaelSimpleClock extends java.applet.Applet {
Date TheDate = new Date();
Button DateButton = new Button(
" Click me! ");
public void init() {
add(DateButton);
}
public boolean handleEvent(Event e) {
if (e.target == DateButton) {
DateButton.setLabel(TheDate.toString());
}
return true;
}
}
Figure 13.1 shows the MichaelSimpleClock applet. Note
that the clock in the applet is wrong: it is not actually 8:00
a.m. There is no way I would write that early in the morning.
Figure 13.1 : The MichaelSimpleClock applet.
What about a real-time clock that updates as the clock changes?
To accomplish this small feat, you must include in the applet
a loop that has each iteration reconstructing the internal Date
instance. Then, you regularly repaint that value inside the applet's
paint() method. You also have to include threading to
keep your system from locking up during the applet's execution.
Threading is covered in Chapter 9 "Threads and Multithreading,"
so a real-time clock was not included in this chapter.
For the programming of games and many other program types, it
is important to be able to generate random numbers. Java includes
the capability to generate random numbers efficiently and effectively.
The Random class implements a pseudo-random number data
type that generates a stream of seemingly random numbers. To create
a sequence of different pseudo-random values each time the application
is run, create the Random object as follows:
Random r=new Random();
This statement seeds the random generator with the current time.
On the other hand, consider the following statement:
Random r=new Random(326); // Pick any value
This statement seeds the random generator with the same value
each time, resulting in the same sequence of pseudo-random numbers
each time the application runs. The generator can be reseeded
at any time using the setSeed() method.
TIP |
Want to get really random numbers? Well, you can't. But a common practice to simulate actual random numbers in computer programs is to seed the random number generator with some variant of the current time or date. If, for example, you want to seed a random number generator with the sum of the current seconds, minutes, and hours, you could use this code, which should suffice for most tasks:
int OurSeed = ADate.getSeconds() + ADate.getHours() +
ADate.getMinutes(); Random = new Random(OurSeed);
|
Pseudo-random numbers can be generated using one of these functions:
nextInt(), nextLong(), nextFloat(),
nextDouble(), or nextGaussian(). The first four
functions return integers, longs, floats,
and doubles. For more information about the Gaussian distribution,
refer to the following sidebar. The program Random1 in
Listing 13.5 prints out pseudo-random uniformly distributed values
using these functions.
Listing 13.5. Random1.java: A sample Random
program.
import java.lang.Math;
import java.util.Date;
import java.util.Random;
class Random1 {
public static void main(String args[])
throws java.io.IOException
{
int count=6;
Random randGen=new Random();
System.out.println("Uniform Random Integers");
for (int i=0;i<count;i++)
System.out.print(randGen.nextInt()+" ");
System.out.println("\n");
System.out.println("Uniform Random Floats");
for (int i=0;i<count;i++)
System.out.print(randGen.nextFloat()+" ");
System.out.println("\n");
System.out.println("Gaussian Random Floats");
for (int i=0;i<count;i++)
System.out.print(randGen.nextGaussian()+" ");
System.out.println("\n");
System.out.println("Uniform Random Integers [1,6]");
for (int i=0;i<count;i++)
System.out.print((Math.abs(randGen.nextInt())%6+1)+" ");
System.out.println("\n");
}
}
The output from the preceding program looks like this:
Uniform Random Integers
1704667569 -1431446235 1024613888 438489989 710330974 -1689521238
Uniform Random Floats
0.689189 0.0579988 0.0933537 0.748228 0.400992 0.222109
Gaussian Random Floats
-0.201843 -0.0111578 1.63927 0.205938 -0.365471 0.626304
Uniform Random Integers [1,6]
4 6 1 6 3 2
If you want to generate uniformly distributed random integers
within a specific range, the output from nextInt(), nextLong(),
or nextDouble() can be scaled to match the required range.
However, a simpler approach is to take the remainder of the result
of nextInt() divided by the number of different values
plus the first value of the range. For example, if the values
10 to 20 are needed, you can use the formula nextInt()%21+10.
Unfortunately, although this method is much simpler than scaling
the output of nextInt(), it is guaranteed to work only
on truly random values. Because the pseudo-random generator may
have various undesired correlations, the
modulus operator may not provide acceptable results--you
might get all odd numbers, for example. In other words, don't
plan on simulating the detonation of your new H-bomb in Java because
you may find yourself a couple miles too close.
GAUSSIAN and NORMAL DISTRIBUTIONS
|
Uniformly distributed random numbers are generated using a modified linear congruential method with a 48-bit seed. Uniformly distributed random numbers within a given range all appear with the same frequency. The Random class can also generate random numbers in a Gaussian or Normal distribution. The Gaussian frequency distribution curve is also referred to as a bell curve. For information on the Gaussian frequency distribution curve, see The Art of Computer Programming, Volume 2, by Donald Knuth
|
Table 13.5 summarizes the complete interface of the Random
class.
Table 13.5. The methods available in the Random
interface.
Method | Description
|
Constructors
|
Random() | Creates a new random number generator
|
Random(long) | Creates a new random number generator using a seed
|
Methods |
nextDouble() | Returns a pseudo-random uniformly distributed double
|
nextFloat() | Returns a pseudo-random uniformly distributed float
|
nextGaussian() | Returns a pseudo-random Gaussian distributed double
|
nextInt() | Returns a pseudo-random uniformly distributed integer
|
nextLong() | Returns a pseudo-random uniformly distributed long
|
setSeed(long) | Sets the seed of the pseudo-random number generator
|
The applet shown in Listing 13.6 demonstrates a bit of what you
can do with the Random class.
Listing 13.6. Using the Random class.
import java.awt.*;
import java.util.*;
public class TheWanderer extends java.applet.Applet {
int xpos = 100;
int ypos = 100;
// Our current date
Date D = new Date();
// The movement button
Button theButton = new Button("Click Me");
// Our random number generator
Random R;
public void init() {
add(theButton);
setBackground(Color.white);
// Our random number generator seeded with the current seconds
int seed = D.getSeconds();
R = new Random(seed);
}
public boolean handleEvent (Event e) {
if (e.target == theButton) {
// Move our thing.
xpos = xpos + (Math.abs(R.nextInt())%10-7);
ypos = ypos + (Math.abs(R.nextInt())%10-7);
// repaint the sucker
repaint();
}
return super.handleEvent(e);
}
public void paint(Graphics g) {
g.setColor(Color.black);
g.fillOval(xpos,ypos, 50, 50);
}
}
Figure 13.2 shows TheWanderer applet during its execution.
Figure 13.2 : TheWanderer applet.
This section describes the function of the StringTokenizer
class, which also could have been appropriately grouped with other
classes in Chapter 14, "The I/O Package," because it
is so vital to the input and output functions demonstrated in
that chapter. The StringTokenizer class enables you to
parse a string into a number of smaller strings called tokens.
This class works specifically for what is called "delimited
text," which means that each individual substring of the
string is separated by a delimiter. The delimiter can be anything
ranging from a * to YabaDaba. You simply specify
what you want the class to look for when tokenizing the string.
This class is included in this chapter because it has uses that
prove helpful in everything from a spreadsheet applet to an arcade
game applet.
The delimiter set can be specified when the StringTokenizer
object is created, or it can be specified on a per-token basis.
The default delimiter set is the set of whitespace characters.
With this delimiter set, the class would find all the separate
words in a string and tokenize them. For example, the StringTokenizer1
code in Listing 13.7 prints out each word of the string on a separate
line.
Listing 13.7. StringTokenizer1.java: A sample
StringTokenizer program.
import java.io.DataInputStream;
import java.util.StringTokenizer;
class StringTokenizer1 {
public static void main(String args[])
throws java.io.IOException
{
DataInputStream dis=new DataInputStream(System.in);
System.out.println("Enter a sentence: ");
String s=dis.readLine();
StringTokenizer st=new StringTokenizer(s);
while (st.hasMoreTokens())
System.out.println(st.nextToken());
}
}
Here is the output from this listing:
Enter a sentence:
Four score and seven
Four
score
and
seven
Pure excitement. The method countTokens() returns the
number of tokens remaining in the string using the current delimiter
set--that is, the number of times nextToken() can be
called before generating an exception. This is an efficient method
because it does not actually construct the substrings that nextToken()
must generate.
In addition to extending the java.lang.object class,
the StringTokenizer class implements the java.util.Enumeration
interface.
Table 13.6 summarizes the methods of the StringTokenizer
class.
Table 13.6. The methods available in the StringTokenizer
interface.
Method | Description
|
Constructors
|
StringTokenizer(string) | Constructs a StringTokenizer given a string using whitespace as delimiters.
|
StringTokenizer(string, string) |
Constructs a StringTokenizer given a string and a delimiter set.
|
StringTokenizer (string, string, boolean)
| Constructs a StringTokenizer given a string and a delimiter set. The final parameter is a boolean value which, if true, says that the delimiters must be returned as tokens. If this parameter is false, the tokens are not returned.
|
Methods |
countTokens() | Returns the number of tokens remaining in the string.
|
hasMoreTokens() | Returns true if more tokens exist.
|
nextToken() | Returns the next token of the string.
|
nextToken(string) | Returns the next token, given a new delimiter set.
|
hasMoreTokens() | Returns true if more elements exist in the enumeration.
|
nextElement() | Returns the next element of the enumeration using the current delimiter set.
|
As stated earlier in this chapter, Java doesn't include dynamically
linked list, queue, or other data structures of that type. Instead,
the designers of Java envisioned the Vector class, which
handles the occasions when you need to dynamically store objects.
Of course, there are positive and negative consequences of this
decision by the designers at Sun. On the positive side, the Vector
class contributes to the simplicity of the language. The major
negative point is that, at face value, the Vector class
severely limits programmers from using more sophisticated programs.
In any case, the Vector class implements a dynamically
allocated list of objects. It attempts to optimize storage by
increasing the storage capacity of the list when needed by increments
larger than just one object. Typically, with this mechanism, there
is some excess capacity in the list. When this capacity is exhausted,
the list is reallocated to add another block of objects at the
end of the list. Setting the capacity of the Vector object
to the needed size before inserting a large number of objects
reduces the need for incremental reallocation. Because of this
mechanism, it is important to remember that the capacity
(the available elements in the Vector object) and the
size (the number of elements currently stored in the Vector
object) usually are not the same.
Suppose that a Vector with capacityIncrement
equal to 3 has been created. As objects are added to
the Vector, new space is allocated in chunks of three
objects. After five elements have been added, there is still room
for one more element without the need for any additional memory
allocation.
After the sixth element has been added, there is no more excess
capacity. When the seventh element is added, a new allocation
is made to add three additional elements, giving a total capacity
of nine. After the seventh element is added, there are two remaining
unused elements.
The initial storage capacity and the capacity increment can both
be specified in the constructor. Even though the capacity is automatically
increased as needed, the ensureCapacity() method can
be used to increase the capacity to a specific minimum number
of elements; the trimToSize() method can be used to reduce
the capacity to the minimum number of elements needed to store
the current amount. New elements can be added to the Vector
using the addElement() and insertElementAt()
methods. The elements passed to be stored in the Vector
must be derived from type Object. Elements can be changed
using the setElementAt() method. Removal of elements
is accomplished with the removeElement(), removeElementAt(),
and removeAllElements() methods. Elements can be accessed
directly using the elementAt(), firstElement(),
and lastElement() methods; elements can be located using
the indexOf() and lastIndexOf() methods. Information
about the size and the capacity of the Vector are returned
by the size() and capacity() methods. The setSize()
method can be used to directly change the size of the Vector.
For example, the Vector1 code in Listing 13.8 creates
a Vector of integers by adding new elements to the end.
Then, using a variety of techniques, it prints the Vector.
Listing 13.8. Vector1.java: A sample Vector
program.
import java.lang.Integer;
import java.util.Enumeration;
import java.util.Vector;
class Vector1 {
public static void main(String args[]){
Vector v=new Vector(10,10);
for (int i=0;i<20;i++)
v.addElement(new Integer(i));
System.out.println("Vector in original order using an Enumeration");
for (Enumeration e=v.elements();e.hasMoreElements();)
System.out.print(e.nextElement()+" ");
System.out.println();
System.out.println("Vector in original order using elementAt");
for (int i=0;i<v.size();i++)
System.out.print(v.elementAt(i)+" ");
System.out.println();
// Print out the original vector
System.out.println("\nVector in reverse order using elementAt");
for (int i=v.size()-1;i>=0;i++)
System.out.print(v.elementAt(i)+" ");
System.out.println();
// Print out the original vector
System.out.println("\nVector as a String");
System.out.println(v.toString());
}
}
The output from this program looks like this:
Vector in original order using an Enumeration
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
Vector in original order using elementAt
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
Vector in reverse order using elementAt
19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
Vector as a String
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
NOTE |
The expression new Integer() was used to create integer objects to store because the fundamental types, such as int, are not objects in Java. This technique is used many times throughout this chapter.
|
Notice the use of the Enumeration object as one way to
access the elements of a Vector. Look at the following
lines:
for (Enumeration e=v.elements();e.hasMoreElements();)
System.out.print(e.nextElement()+" ");
You can see that an Enumeration object, which represents
all the elements in the Vector, is created and returned
by the Vector method elements(). With this Enumeration
object, the loop can check to see whether there are more elements
to process using the Enumeration method hasMoreElements();
the loop can get the next element in the Vector using
the Enumeration method nextElement().
The Vector2 program in Listing 13.9 shows some of the
vector-accessing techniques. It first generates a vector of random
integers; then it allows the user to search for a specific value.
The locations of the first and last occurrences of the value are
printed by the program using the indexOf() and lastIndexOf()
methods.
Listing 13.9. Vector2.java: Another sample
Vector program.
import java.io.DataInputStream;
import java.lang.Integer;
import java.lang.Math;
import java.util.Enumeration;
import java.util.Random;
import java.util.Vector;
class Vector2 {
public static void main(String args[])
throws java.io.IOException
{
int numElements;
DataInputStream dis=new DataInputStream(System.in);
Vector v=new Vector(10,10);
Random randGen=new Random();
System.out.println("How many random elements? ");
numElements=Integer.valueOf(dis.readLine()).intValue();
for (int i=0;i<numElements;i++)
v.addElement(new Integer(Math.abs(
randGen.nextInt())%numElements));
System.out.println(v.toString());
Integer searchValue;
System.out.println("Find which value? ");
searchValue=Integer.valueOf(dis.readLine());
System.out.println("First occurrence is element "+
v.indexOf(searchValue));
System.out.println("Last occurrence is element "+
v.lastIndexOf(searchValue));
}
}
The output from this program looks like this:
How many random elements?
10
[0, 2, 8, 4, 9, 7, 8, 6, 3, 2]
Find which value?
8
First occurrence is element 2
Last occurrence is element 6
In addition to extending the java.lang.Object class,
the Vector class implements the java.lang.Cloneable
interface. Table 13.7 summarizes the methods of the Vector
class.
Table 13.7. The variables and methods available in
the Vector interface.
Variable or Method |
Description |
Variables
|
capacityIncrement | Size of the incremental allocations, in elements
|
elementCount | Number of elements in Vector
|
elementData | Buffer in which the elements are stored
|
Constructors
|
Vector() | Constructs an empty vector
|
Vector(int) | Constructs an empty vector with the specified storage capacity
|
Vector(int, int) | Constructs an empty vector with the specified storage capacity and capacityIncrement
|
Methods |
addElement(Object) | Adds the specified object at the end of the Vector
|
capacity() | Returns the capacity of the Vector
|
clone() | Creates a clone of the Vector
|
contains(Object) | True if the specified object is in the Vector
|
copyInto(Object[]) | Copies the elements of this vector into an array
|
elementAt(int) | Returns the element at the specified index
|
elements() | Returns an Enumeration of the elements
|
ensureCapacity(int) | Ensures that the Vector has the specified capacity
|
firstElement() | Returns the first element of the Vector
|
indexOf(Object) | Returns the index of the first occurrence of the specified object within the Vector
|
indexOf(Object, int) | Returns the index of the specified object within the Vector, starting the search at the index specified and proceeding toward the end of the Vector
|
insertElementAt(Object, int) | Inserts an object at the index specified
|
isEmpty() | True if the Vector is empty
|
lastElement() | Returns the last element of the Vector
|
lastIndexOf(Object) | Returns the index of the last occurrence of the specified object within the Vector
|
lastIndexOf(Object, int) | Returns the index of the specified object within the Vector, starting the search at the index specified and proceeding toward the beginning of the Vector
|
removeAllElements() | Removes all elements of the Vector
|
removeElement(Object) | Removes the specified object from the Vector
|
removeElementAt(int) | Removes the element with the specified index
|
setElementAt(Object, int) | Stores the object at the specified index in the Vector
|
setSize(int) | Sets the size of the Vector
|
size() | Returns the number of elements in the Vector
|
toString() | Converts the Vector to a string
|
trimToSize() | Trims the Vector's capacity down to the specified size
|
The stack data structure is key to many programming efforts, ranging
from building compilers to solving mazes. The Stack class
in the Java library implements a Last In, First Out (LIFO)
stack of objects. Even though they are based on (that is, they
extend) the Vector class, Stack objects are
typically not accessed in a direct fashion. Instead, values are
pushed on and popped off the top of the stack. The net effect
is that the values most recently pushed are the first to pop.
The Stack1 code in Listing 13.10 pushes strings onto
the stack and then retrieves them. The strings end up printed
in the reverse order from which they were stored.
Listing 13.10. Stack1.java: A sample Stack
program.
import java.io.DataInputStream;
import java.util.Stack;
import java.util.StringTokenizer;
class Stack1 {
public static void main(String args[])
throws java.io.IOException
{
DataInputStream dis=new DataInputStream(System.in);
System.out.println("Enter a sentence: ");
String s=dis.readLine();
StringTokenizer st=new StringTokenizer(s);
Stack stack=new Stack();
while (st.hasMoreTokens())
stack.push(st.nextToken());
while (!stack.empty())
System.out.print((String)stack.pop()+" ");
System.out.println();
}
}
The output from this program looks like this:
Enter a sentence:
The quick brown fox jumps over the lazy dog
dog lazy the over jumps fox brown quick The
Even though Stack objects normally are not accessed in
a direct fashion, it is possible to search the Stack
for a specific value using the search() method. search()
accepts an object to find and returns the distance from the top
of the Stack where the object was found. It returns Ð1
if the object is not found.
The peek() method returns the top object on the Stack
without actually removing it from the Stack. The peek()
method throws an EmptyStackException if the stack has
no items.
Table 13.8 summarizes the complete interface of the Stack
class.
Table 13.8. The methods available in the Stack
interface.
Method | Description
|
Constructor
|
Stack() | Constructs an empty Stack
|
Methods |
empty() | True if the Stack is empty
|
peek() | Returns the top object on the Stack without removing the element
|
pop() | Pops an element off the Stack
|
push(Object) | Pushes an element onto the Stack
|
search(Object) | Finds an object on the Stack
|
The Dictionary class is an abstract class used as a base
for the Hashtable class. It implements a data structure
that allows a collection of key and value pairs to be stored.
Any type of object can be used for the keys or the values. Typically,
the keys are used to find a particular corresponding value.
Because the Dictionary class is an abstract class that
cannot be used directly, the code examples presented in this section
cannot actually be run. They are presented only to explain the
purpose and use of the methods declared by this class. The following
code would, hypothetically, be used to create a Dictionary
with these values:
Dictionary products = new Dictionary();
products.put(new Integer(342), "Widget");
products.put(new Integer(124), "Gadget");
products.put(new Integer(754), "FooBar");
The put() method is used to insert a key and value pair
into the Dictionary. Both arguments must be derived from
the class Object. The key is the first argument, and
the value is the second argument.
A value can be retrieved using the get() method and a
specific key to be found. get() returns the null
value if the specified key is not found. Here's an example:
String name = products.get(new Integer(124));
if (name != null) {
System.out.println("Product name for code 124 is " + name);
}
Although an individual object can be retrieved with the get()
method, it is sometimes necessary to access all the keys or all
the values. Two methods, keys() and elements(),
return Enumerations that can be used to access the keys
and the values.
Table 13.9 summarizes the complete interface of the Dictionary
class.
Table 13.9. The methods available in the Dictionary
interface.
Method | Description
|
Constructors
|
Dictionary() | Constructs an empty Dictionary
|
Methods |
elements() | Returns an Enumeration of the values
|
get(Object) | Returns the object associated with the specified key
|
isEmpty() | True if the Dictionary has no elements
|
keys() | Returns an Enumeration of the keys
|
put(Object, Object) | Stores the specified key and value pair in the Dictionary
|
remove(Object) | Removes an element from the Dictionary based on its key
|
size() | Returns the number of elements stored
|
The Hashtable data structure is very useful when searching
for and manipulating data. You should use the Hashtable
class if you will be storing a large amount of data in memory
and then searching it. The time needed to complete a search of
a hash table is decidedly less than what it takes to search a
Vector. Of course, for small amounts of data, it doesn't
make much difference whether you use a hash table or a linear
data structure because the overhead time is much greater than
any search time would be. See the following sidebar for more information
on search times in the different classes.
Hash table organization is based on keys, which are computed
based on the data being stored. For example, if you want to insert
a number of words into a hash table, you can base your key on
the first letter of the word. When you come back later to search
for a word, you can then compute the key for the item being sought.
By using this key, search time is drastically reduced because
the items are stored based on the value of their respective keys.
The Hashtable class implements a hash table storage mechanism
for storing key and value pairs. Hash tables are designed to quickly
locate and retrieve information stored by using a key. Keys and
values can be of any object type, but the key object's class must
implement the hashCode() and equals() methods.
SEARCH TIMES |
Big "O" notation is used to measure the "worst case scenario" time requirements in terms of searching while using different data structures. Linear searching, such as that used in the Vector class, is O(n); hash table searching is O(log n). This means that for a large number of objects, you can save a lot of search time when you use a Hashtable because the log of a number is always less than the number itself. If you will be doing a large amount of searching through data, a hash table is likely to be much more efficient.
|
The sample Hashtable1 in Listing 13.11 creates a Hashtable
object and stores 10 key and value pairs using the put()
method. It then uses the get() method to return the value
corresponding to a key entered by the user.
Listing 13.11. Hashtable1.java: A sample Hashtable
program.
import java.io.DataInputStream;
import java.lang.Integer;
import java.lang.Math;
import java.util.Random;
import java.util.Hashtable;
class Hashtable1 {
public static void main(String args[])
throws java.io.IOException
{
DataInputStream dis=new DataInputStream(System.in);
int numElements=10;
String keys[]={"Red","Green","Blue","Cyan","Magenta",
"Yellow","Black","Orange","Purple","White"};
Hashtable ht;
Random randGen=new Random();
ht=new Hashtable(numElements*2);
for (int i=0;i<numElements;i++)
ht.put(keys[i],new Integer(Math.abs(
randGen.nextInt())%numElements));
System.out.println(ht.toString());
String keyValue;
System.out.println("Which key to find? ");
keyValue=dis.readLine();
Integer value=(Integer)ht.get(keyValue);
if (value!=null) System.out.println(keyValue+" = "+value);
}
}
The output from this program looks like this:
{Cyan=4, White=0, Magenta=4, Red=5, Black=3,
Green=8, Purple=3, Orange=4, Yellow=2, _Blue=6}
Which key to find?
Red
Red = 5
In addition to the get() method, the contains()
and containsKey() methods can be used to search for a
particular value or key. Both return true or false
depending on whether the search was successful. The contains()
method must perform an exhaustive search of the table and is not
as efficient as the containsKey() method, which can take
advantage of the hash table's storage mechanism to find the key
quickly.
Because hash tables must allocate storage for more data than actually
is stored, a measurement called the load factor indicates
the number of used storage spaces as a fraction of the total available
storage spaces. The load factor is expressed as a value between
0 and 100 percent. Typically, the load factor should not be higher
than about 50 percent for efficient retrieval of data from a hash
table. When specifying the load factor in a program, use a fractional
value in the range 0.0 to 1.0 to represent load factors in the
range 0 to 100 percent.
Hash tables can be constructed in three different ways: By specifying
the desired initial capacity and load factor; by specifying only
the initial capacity; or by specifying neither the initial capacity
nor the load factor. If the load factor is not specified, the
Hashtable is rehashed into a larger table when it is
full--otherwise, it is rehashed when it exceeds the load factor.
The constructors throw an IllegalArgumentException if
the initial capacity is less than or equal to zero, or if the
load factor is less than or equal to zero.
The clone() method can be used to create a copy (a clone)
of the Hashtable. However, it creates a shallow copy
of the Hashtable, which means that the keys and values
themselves are not clones. This local method overrides the inherited
clone() method.
CAUTION |
The clone() method is a relatively expensive operation to perform in terms of memory usage and execution time. Because the new Hashtable still refers directly to the objects (keys and values) stored in the old table, use caution to avoid making changes that will disrupt the original Hashtable
|
The Hashtable class extends the java.util.Dictionary
class and implements the java.lang.Cloneable interface.
Table 13.10 summarizes the methods of the Hashtable class.
Table 13.10. The methods available in the Hashtable
interface.
Method | Description
|
Constructors
|
Hashtable() | Constructs an empty Hashtable
|
Hashtable(int) | Constructs an empty Hashtable with the specified capacity
|
Hashtable(int, float) | Constructs an empty Hashtable with the given capacity and load factor
|
Methods |
clear() | Deletes all elements from the Hashtable
|
clone() | Creates a clone of the Hashtable
|
contains(Object) | True if the specified object is an element of the Hashtable
|
containsKey(Object) | True if the Hashtable contains the specified key
|
elements() | Returns an Enumeration of the Hashtable's values
|
get(Object) | Returns the object associated with the specified key
|
isEmpty() | True if the Hashtable has no elements
|
keys() | Returns an Enumeration of the keys
|
put(Object, Object) | Stores the specified key and value pair in the Hashtable
|
rehash() | Rehashes the contents of the table into a bigger table
|
remove(Object) | Removes an element from the Hashtable based on its key
|
size() | Returns the number of elements stored
|
toString() | Converts the contents to a very long string
|
The Properties class is what enables end-users to customize
their Java programs. For example, you can easily store values
such as foreground colors, background colors, font defaults, and
so on and then have those values available to be reloaded. This
arrangement is most useful for Java applications, but you can
also implement them for applets. If you have an applet that is
regularly used by multiple users, you can keep a properties file
on your server for each different user; the properties file is
accessed each time that user loaded the applet.
The Properties class is a Hashtable, which can
be repeatedly stored and restored from a stream. It is used to
implement persistent properties. The Properties class
also allows for an unlimited level of nesting by searching a default
property list if the required property is not found. The fact
that this class is an extension of the Hashtable class
means that all methods available in the Hashtable class
are also available in the Properties class.
The sample Properties1 program in Listing 13.12 creates
two properties lists. One is the default property list and the
other is the user-defined property list. When the user property
list is created, the default Properties object is passed.
When the user property list is searched, if the key value is not
found, the default Properties list is searched.
Listing 13.12. Properties1.java: A sample Properties
program.
import java.io.Data7InputStream;
import java.lang.Integer;
import java.util.Properties;
class Properties1 {
public static void main(String args[])
throws java.io.IOException
{
int numElements=4;
String defaultNames[]={"Red","Green","Blue","Purple"};
int defaultValues[]={1,2,3,4};
String userNames[]={"Red","Yellow","Orange","Blue"};
int userValues[]={100,200,300,400};
DataInputStream dis=new DataInputStream(System.in);
Properties defaultProps=new Properties();
Properties userProps=new Properties(defaultProps);
for (int i=0;i<numElements;i++){
defaultProps.put(defaultNames[i],
Integer.toString(defaultValues[i]));
userProps.put(userNames[i],
Integer.toString(userValues[i]));
}
System.out.println("Default Properties");
defaultProps.list(System.out);
System.out.println("\nUser Defined Properties");
userProps.list(System.out);
String keyValue;
System.out.println("\nWhich property to find? ");
keyValue=dis.readLine();
System.out.println("Property '"+keyValue+"' is '"+
userProps.getProperty(keyValue)+"'");
}
}
Notice that the getProperties() method is used instead
of the inherited get() method. The get() method
searches only the current Properties object. The getProperties()
method must be used to search the default Properties
list. An alternative form of the getProperties() method
has a second argument: a Properties list that is to be
searched instead of the default specified when the Properties
object was created.
The propertyNames() method can be used to return an Enumeration,
which can be used to index through all the property names. This
Enumeration includes the property names from the default
Properties list. Likewise, the list() method,
which prints the Properties list to the standard output,
lists all the properties of the current Properties object
and those in the default Properties object.
Properties objects can be written to and read from a
stream using the save() and load() methods.
In addition to the output or input stream, the save()
method has an additional string argument that is written at the
beginning of the stream as a header comment.
Table 13.11 summarizes the methods of the Properties
class.
Table 13.11. The variables and methods available in
the Properties interface.
Variable or Method |
Description |
Variable |
defaults | Default Properties list to search
|
Constructors
|
Properties() | Constructs an empty property list
|
Properties(Properties) | Constructs an empty property list with the specified default
|
Methods |
getProperty(string) | Returns a property given the key
|
getProperty(string, string) | Returns a property given the specified key and default
|
list(PrintStream) | Lists the properties to a stream for debugging
|
load(InputStream) | Reads the properties from an InputStream
|
propertyNames() | Returns an Enumeration of all the keys
|
save(OutputStream, string) | Writes the properties to an OutputStream
|
The Observable class acts as a base class for objects
you want to have observed by other objects that implement the
Observer interface. An Observable object can
notify its Observers whenever the Observable
object is modified using the notifyObservers() method.
This method accomplishes the notification by invoking the update()
method of all its Observers, optionally passing a data
object that is passed to notifyObservers(). Observable
objects can have any number of Observers.
Table 13.12 summarizes the complete interface of the Observable
class.
Table 13.12. The methods available in the Observable
interface.
Method | Description
|
Constructor
|
Observable() | Constructs an instance of the Observable class
|
Methods |
addObserver(Observer) | Adds an Observer to the observer list
|
clearChanged() | Clears an observable change
|
countObservers() | Returns the number of Observers
|
deleteObserver(Observer) | Deletes an Observer from the observer list
|
deleteObservers() | Deletes all Observers from the observer list
|
hasChanged() | True if an observable change has occurred
|
notifyObservers() | Notifies all observers when an observable change has occurred
|
notifyObservers(Object) | Notifies all observers of a specific observable change
|
setChanged() | Sets a flag to indicate that an observable change has occurred
|
This chapter described the classes that make up the Java utilities
package. This package provides complete implementations of the
basic data structures and some of the most useful data types (other
than the fundamental numeric types) needed by programmers. Many
of the data types and data structures that you will develop using
Java will be based on the classes found in the utilities package.
For smaller applets, many of these classes are not necessary.
However, as your applets increase in complexity, you will find
these classes to be very useful. In any case, this chapter has
been a good starting point for understanding the utility of these
important Java classes and for understanding how to use them effectively.
Contact
reference@developer.com with questions or comments.
Copyright 1998
EarthWeb Inc., All rights reserved.
PLEASE READ THE ACCEPTABLE USAGE STATEMENT.
Copyright 1998 Macmillan Computer Publishing. All rights reserved.