TOC
BACK
FORWARD
HOME

Java 1.1 Unleashed

- 11 -
The Utilities Package

by Michael Girdley and Richard Lesh revised by Billy Barron

IN THIS CHAPTER

  • Linked Lists, Queues, Search Trees, and Other Dynamic Data Structures
  • Using the Utilities Package
  • Classes

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 most 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 11.1 shows the classes that are part of the utilities package and that are discussed in this chapter.

Table 11.1. Important classes in the utilities package.

Class Description
BitSet Implements a collection of binary values
Calendar Used to implement a calendar
Date Used for date and time data storage and use
Dictionary Used to store a collection of key and value pairs
GregorianCalendar Used to implement a Gregorian calendar
Hashtable Used to store a hash table
Locale Used to implement a location
Observable Used to store observable data
Properties Used for storage of a properties list that can be saved
Random Used to generate a pseudo-random number
SimpleTimeZone Used to implement a simplified time zone
Stack Used to store and implement a stack
StringTokenizer Used to tokenize a string
TimeZone Used to store information about a time zone
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."

Linked Lists, Queues, Search Trees, and Other Dynamic Data Structures

One would expect that the Vector class would eliminate the necessity for creating your own data structures. But there may be times when you may 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 need 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 just as 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 the list goes 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.

Using the Utilities Package

The utilities package has three interfaces, but you typically use only two: Enumeration and Observer. The other interface is EventListener; it is most commonly used internally by the AWT for handling events. Programmers only rarely implement the EventListener interface. 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

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 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

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 only specifies 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.

Classes

The utilities package supplies many 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

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 11.1 shows the basic BitSet operations.

Listing 11.1. BitSet1.java: a sample BitSet program.

import java.io.DataInputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.BitSet;
class BitSet1 {
    public static void main(String args[])
        throws java.io.IOException
    {
        BufferedReader dis=new BufferedReader(new InputStreamReader(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 11.2 summarizes all the methods available in the BitSet class.

Table 11.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.

The Calendar Class

The Calendar class is an abstract class used to convert dates. You can use this class to convert a Date object to fields, such as YEAR, MONTH, HOUR, and so on. You can also use these fields to update a Date object.

In the API definition, only one subclass of Calendar exists: GregorianCalendar. Because most people and virtually all businesses in the world use the Gregorian calendar, you will find all the details about calendars in that section, later in this chapter.

Table 11.3 summarizes the methods available in the Calendar class.

Table 11.3. Methods in the Calendar class.

Method Description

Constructors

Calendar() Creates a calendar with the default TimeZone and Locale
Calendar(TimeZone,Locale) Creates a calendar with the given TimeZone and Locale

Static Methods

getDefault() Returns a calendar with the default TimeZone and Locale
getDefault(TimeZone) Returns a calendar with the default Locale and given TimeZone
getDefault(Locale) Returns a calendar with the given Locale and default TimeZone
getDefault(TimeZone,Locale) Returns a calendar with the given TimeZone and Locale
getAvailableLocales() Returns an array of all available locales

Methods

getTime() Returns the date and time
setTime(Date) Sets the date and time
get(byte) Returns the specified field
set(byte,int) Sets the specified field to the specified value
set(int,int,int) Sets the year, month, and date
set(int,int,int,int,int) Sets the year, month, date, hour, and minute
set(int,int,int,int,int,int) Sets the year, month, date, hour, minute, and second
clear() Clears all fields
clear(byte) Clears the specified field
isSet(int) Returns true if the specified field is set
equals(Object) Returns true if two objects are the same
before(Object) Returns true if this object is before the given object
after(Object) Returns true if this object is after the given object
add(byte,int) Adds the given value to the field
roll(byte,boolean) Increments or decrements (depending on the boolean value) the specified field by one unit
setTimeZone(TimeZone) Sets the time zone this object is in
setValidationMode(boolean) If the boolean value is set to true, invalid dates are allowed
getValidationMode() Returns whether or not invalid dates are allowed
setFirstDayOfWeek(byte) Sets the first day of the week
getFirstDayOfWeek() Returns the first day of the week
setMinimumDaysInFirstWeek(byte) Sets how many days are required to define the first week of the month
getMinimumDaysInFirstWeek() Returns how many days are required to define the first week of the month
getMinimum(byte) Returns the minimum possible value for the given field
getMaximum(byte) Returns the maximum possible value for the given field
getGreatestMinimum(byte) Returns the greatest possible minimum value for the field
getLeastMaximum(byte) Returns the least possible maximum value for the given field
Clone() Makes a copy of this object


NOTE: The use of the terms greatest minimum and least maximum may have confused you in Table 11.3. An example of least maximum is the question "what is the least maximum day in any month?" The answer is 28--from February, where the maximum day is 28 (in most years). For the Gregorian calendar (which most of us use), getGreatestMinimum() always returns the same value as getMinimum().

The Date Class

Before Java 1.1, the Date class was an extremely important class for handling dates and times. However, it was weak in some areas such as internationalization and dealing with the differences in daylight saving time in different locations.

With Java 1.1, the Date class has been relegated to just storing the exact time. Most of the old Date class methods have been deprecated and should no longer be used. All the methods for converting time between binary and human-readable form are now handled by the Calendar, GregorianCalendar, TimeZone, SimpleTimeZone, and Locale classes. An example of how these classes can work together is found in Listing 11.3, in "The GregorianCalendar Class," later in this chapter.

The default constructor is used when the current date and time are required. The other constructor takes a millisecond representation of time and creates a Date object based on it.

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.

Dates can be compared to each other by using their UTC values (the UTC value is the number of seconds since January 1, 1970) or by using the methods after(), before(), and 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 multiplied by 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 11.4 summarizes all the nondeprecated methods available in the Date class. You can find the deprecated methods by looking at the API reference included with the JDK or found on JavaSoft's Web site (http://www.javasoft.com/).

Table 11.4. Useable 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

Methods

after(Date) Returns true if the date is later than the specified date
before(Date) Returns true if the date is earlier than the specified date
equals(Object) Returns true if the date and the specified date are equal
getTime() Returns the time as a single UTC value
hashCode() Computes a hash code for the date
setTime(long) Sets the time using a single UTC value
toString() Converts a date to text using UNIX ctime() conventions


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 11.2 demonstrates the use of the Date class.

Listing 11.2. Using the Date class.

import java.awt.*;
import java.util.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class MichaelSimpleClock extends java.applet.Applet {
    Button DateButton = new Button(
        "                   Click me!                     ");
    public void init()  {
        ButtonListener bl = new ButtonListener(DateButton);
        add(DateButton);
     DateButton.addActionListener(bl);
    }
}

class ButtonListener implements ActionListener {
    Date TheDate = new Date();
    Button theButton;

    public ButtonListener(Button aButton) {
     theButton = aButton;
    }

    public void actionPerformed (ActionEvent e) {
            theButton.setLabel(TheDate.toString());
    }
}

Figure 11.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 11.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 6, "Threads and Multithreading," so a real-time clock was not included in this chapter.

The GregorianCalendar Class

Before 1582, the western world used the Julian calendar system, which was adopted by Julius Caesar. The problem with the Julian calendar was that it had too many leap years (3 too many every 400 years). Pope Gregory XIII fixed that problem. In addition, the Julian year started on March 25; the Gregorian calendar year starts on January 1. At the time of the switch, the last day of the Julian calendar was October 4, 1582, and the first day of the Gregorian calendar was October 15, 1582. The dates in between never occurred. However, this was not true for England and America, which kept the Julian calendar until 1752. When England and America switched, the last Julian day was September 2, 1752, and the first Gregorian date was September 14, 1752. If you are working on historical dates, I strongly recommend that you do some additional reading on this subject!

The GregorianCalendar class handles conversions between actual dates and data fields such as month for the Gregorian calendar. The Gregorian calendar is the most widely used calendar system in the world. However, in fact, this class also handles the Julian calendar system.

Table 11.5 summarizes the methods available in the GregorianCalendar class.

Table 11.5. Methods in the GregorianCalendar class.

Method Description

Constructors

GregorianCalendar() Creates a Gregorian calendar using the current time with the default TimeZone and Locale
GregorianCalendar(TimeZone) Creates a Gregorian calendar using the current time with the default Locale and the given TimeZone
GregorianCalendar(TimeZone,Locale) Creates a Gregorian calendar with the current time and the given TimeZone and Locale
GregorianCalendar(int,int,int) Creates a Gregorian calendar at the given year, month, and date with the default TimeZone and Locale
GregorianCalendar(int,int,int,int,int) Creates a Gregorian calendar at the given year, month, date, hour, and minute with the default TimeZone and Locale
GregorianCalendar(int,int,int,int,int,int) Creates a Gregorian calendar at the given year, month, date, hour, minute, and second with the default TimeZone and Locale
GregorianCalendar(Locale) Creates a Gregorian calendar at the current time with the default TimeZone and the given Locale

Methods

setGregorianChange(Date) Sets the date that the Julian calendar changed to the Gregorian calendar
getGregorianChange() Returns the date that the Julian calendar changed to the Gregorian calendar
isLeapYear() Returns true if the year of this object is a leap year
equals(Objects) Returns true if the two objects are equal
before(Object) Returns true if this object is before the date of the object given
after(Object) Returns true if the object is after the date of the object given
add(byte,amount) Adds the amount given to the field specified by byte
roll(byte,boolean) Increments or decrements (depending on the boolean value) the specified field by one unit
getMinimum(byte) Returns the minimum value for the specified field
getMaximum(byte) Returns the maximum value for the specified field
getGreatestMinimum(byte) Returns the greatest minimum value for the specified field
getLeastMaximum(byte) Returns the least maximum value for the specified field
clone() Makes a copy of this object


Listing 11.3 shows the use of the GregorianCalendar class as well as many other classes in the utilities package.

Listing 11.3. A program to calculate the day of week.

import java.util.*;
public class cal {
   public static void main(String args[]) {
       int msecsInHour = 60*60*1000;
       SimpleTimeZone cst = new SimpleTimeZone(-6*msecsInHour,"CST");
       cst.setStartRule(Calendar.APRIL, 1, Calendar.SUNDAY, 2*msecsInHour);
       cst.setEndRule(Calendar.OCTOBER, -1, Calendar.SUNDAY, 2*msecsInHour);
       Calendar calendar = new GregorianCalendar(cst);

       System.out.println("Day of Week: " + calendar.get(Calendar.DAY_OF_WEEK));
   }
}

The Locale Class

The Locale class is used to define a locale. A locale is a combination of a geographic location, a language, and a variant. The variant is usually a browser-specific code. The Locale class is key in using internationalization in your Java code. The Locale class enables you to write your program independent of country and language. Specific behaviors for each country and language can easily be added later when they are needed.

The ISO (International Standards Organization) language codes are documented in standard ISO-639 (http://www.ics.uci.edu/pub/ietf/http/related/iso639.txt). The ISO country codes are documented in standard ISO-3166 (http://www.chemie.fu-berlin.de/diverse/doc/ISO_3166.html). An example of a country code is "US" (for the United States). An example of a language code is "EN" (for English). Some example of variants are WIN (for Windows), MAC (for Macintosh), and POSIX (for UNIX).

Table 11.6 lists the methods available in the Locale class.

Table 11.6. Methods available with the Locale class.

Method Description

Constructor

Locale(String, String) Creates a locale based on a given language and country
Locale(String, String, String) Creates a locale based on a given language, country, and variant

Static Methods

getDefault() Returns the default Locale
setDefault(Locale) Sets the default Locale

Methods

getLanguage() Returns the language in use
getCountry() Returns the country in use
getISO3Language() Returns the three-character ISO code for the language
getISO3Country() Returns the three-character ISO code for the country
getVariant() Returns the variant in use
toString() Returns the object as a String
getDisplayLanguage() Returns the language to display to the user
getDisplayLanguage(Locale) Returns the language to display to the user based on the given Locale
getDisplayCountry() Returns the country to display to the user
getDisplayCountry(Locale) Returns the country to display to the user for the given Locale
getDisplayVariant() Returns the variant to display to the user
getDisplayVariant(Locale) Returns the variant to display to the user for the given Locale
getDisplayName() Returns the language, country, and variant to display to the user
getDisplayName(Locale) Returns the language, country, and variant to display to the user for the given Locale
clone() Makes a copy of this locale
hashCode() Returns the hash code for this locale
equals(Object) Returns true if two locales are equal

The Random Class

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 Random1 program in Listing 11.4 prints five pseudo-random uniformly distributed values using these functions.

Listing 11.4. 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 11.7 summarizes the complete interface of the Random class.

Table 11.7. 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 11.5 demonstrates a bit of what you can do with the Random class.

Listing 11.5. Using the Random class.

import java.awt.*;
import java.util.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

public class TheWanderer extends java.applet.Applet {
        int xpos = 100;
        int ypos = 100;
    // Our current date.
    Calendar C = new GregorianCalendar();
        // The movement button
        Button theButton = new Button("Click Me");
        // Our random number generator.
        Random R;
        public void init() {
                SimpleListener simple = new SimpleListener(this);
                add(theButton);
                theButton.addActionListener(simple);
                setBackground(Color.white);
        // Our random number generator seeded with the current seconds.
        int seed = C.get(Calendar.SECOND);
                R = new Random(seed);
        }
    public void paint(Graphics g) {
                g.setColor(Color.black);
                g.fillOval(xpos,ypos, 50, 50);
    }

    public void move() {
                        // Move our thing.
                        xpos = xpos + (Math.abs(R.nextInt())%10-7);
                        ypos = ypos + (Math.abs(R.nextInt())%10-7);
            // repaint the sucker.
                        repaint();
    }
}

class SimpleListener implements ActionListener {
        private TheWanderer theClass;

        public SimpleListener(TheWanderer aClass) {
                theClass = aClass;
            }
        public void actionPerformed (ActionEvent e) {
                theClass.move();
            }
    }

Figure 11.2 shows TheWanderer applet during its execution.

Figure 11.2.

TheWanderer applet.

The SimpleTimeZone Class

The SimpleTimeZone class is a simple TimeZone that can be used with the GregorianCalendar class. The SimpleTimeZone class assumes that time-zone rules have not changed historically. Additionally, it assumes that the rules are of a simple nature. Even with these limitations, this class can handle the needs of the majority of programmers. Listing 11.3, earlier in this chapter, provides an example of how to use this class. Table 11.8 summarizes the methods available with the SimpleTimeZone class.

Table 11.8. Methods available with the SimpleTimeZone class.

Method Description

Constructor

SimpleTimeZone(int,String) Creates a TimeZone with the given offset and time-zone name
SimpleTimeZone(int,String,int,int, Creates a TimeZone with the given offset,
int,int,int,int,int,int) time-zone name, and time to start and end daylight saving time

Methods

setStartYear(int) Sets the first year that daylight saving time started
setStartRule(int,int,int,int) Sets the rule for the beginning of daylight saving time based on the month, week, day of week, and time of day
setEndRule(int,int,int,int) Sets the rule for the end of daylight saving time based on the month, week, day of week, and time of day
getOffset(int,int,int,int,int,int) Returns the Greenwich mean time (GMT) offset for a given time
getRawOffset() Returns the GMT offset for the current time
setRawOffset(int) Sets the base GMT offset
useDaylightTime() Returns true if this TimeZone uses daylight saving time
inDaylightTime(Date) Returns true if the given date is in daylight saving time
clone() Makes a copy of this object
hashCode() Returns the hash code for this object
equals(Object) Returns true if this object is equal to the one given

The StringTokenizer Class

This section describes the function of the StringTokenizer class, which also could have been appropriately grouped with the other classes in Chapter 12, "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 an * 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 11.6 prints out each word of the string on a separate line.

Listing 11.6. StringTokenizer1.java: A sample StringTokenizer program.

import java.io.DataInputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
class StringTokenizer1 {
    public static void main(String args[])
        throws java.io.IOException
    {
        BufferedReader dis=new BufferedReader(new InputStreamReader(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 11.9 summarizes the methods of the StringTokenizer class.

Table 11.9. The methods available in the StringTokenizer interface.

Method Description

Constructors

StringTokenizer Constructs a StringTokenizer given a
(String) string using whitespace as delimiters
StringTokenizer Constructs a StringTokenizer given a
(String, String) string and a delimiter set
StringTokenizer Constructs a StringTokenizer given a
(String, String, boolean) 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
hasMoreElements() Returns true if more elements exist in the enumeration
nextElement() Returns the next element of the enumeration using the current delimiter set

The TimeZone Class

The TimeZone class is a generic class that represents any kind of time zone. This class contains the offset from Greenwich mean time (GMT) and can deal with daylight saving time. Table 11.10 lists the methods available with the TimeZone class.

Table 11.10. Methods available with the TimeZone class.

Method Description

Constructor

TimeZone() Creates a TimeZone

Static Methods

getTimeZone(String) Returns the TimeZone object given the name of the time zone
getAvailableIDs() Returns an array of all TimeZone names
getAvailableIDs(int) Returns an array of all TimeZone names that apply to a particular offset
getDefault() Returns the default TimeZone for this machine
setDefault(TimeZone) Sets the default TimeZone for this machine

Methods

getOffset(int,int,int,int,int,int) Returns the GMT offset for the local TimeZone at the time given
getRawOffset() Returns the GMT offset for the local TimeZone at the current time
getID() Returns the name of the TimeZone
setID(String) Sets the name of the TimeZone
useDaylightTime() Returns true if this TimeZone uses daylight saving time
inDaylightTime(Date) Returns true if the date given is in daylight saving time for this TimeZone
clone() Makes a copy of this object

The Vector Class

As stated earlier in this chapter, Java doesn't include dynamically linked lists, queues, 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 11.7 creates a Vector of integers by adding new elements to the end. Then, using a variety of techniques, it prints the Vector.

Listing 11.7. 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 11.8 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 11.8. Vector2.java: Another sample Vector program.

import java.io.DataInputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
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;
        BufferedReader dis=new BufferedReader(new InputStreamReader(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 11.11 summarizes the methods of the Vector class.

Table 11.11. The variables and methods available in the Vector interface.

Variable Description
capacityIncrement Size of the incremental allocations, in elements
elementCount Number of elements in Vector
elementData Buffer in which the elements are stored
Method Description

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 capacity increment

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) Returns 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() Returns 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 Class

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 onto 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 11.9 pushes strings onto the stack and then retrieves them. The strings end up printed in the reverse order from which they were stored.

Listing 11.9. Stack1.java: A sample Stack program.

import java.io.DataInputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Stack;
import java.util.StringTokenizer;
class Stack1 {
    public static void main(String args[])
        throws java.io.IOException
    {
        BufferedReader dis=new BufferedReader(new InputStreamReader(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 method peek() 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 11.12 summarizes the complete interface of the Stack class.

Table 11.12. The methods available in the Stack interface.

Method Description

Constructor

Stack() Constructs an empty Stack

Methods

empty() Returns 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

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 11.13 summarizes the complete interface of the Dictionary class.

Table 11.13. The methods available in the Dictionary interface.

Method Description

Constructor

Dictionary() Constructs an empty Dictionary

Methods

elements() Returns an Enumeration of the values
get(Object) Returns the object associated with the specified key
isEmpty() Returns 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 Class

The hash table 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 about 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 key.

The Hashtable class implements a hash table storage mechanism for storing key and value pairs. Hash tables are designed to quickly locate and retrieve stored information 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 hash table 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 11.10 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 11.10. 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 11.14 summarizes the methods of the Hashtable class.

Table 11.14. 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) Returns true if the specified object is an element of the Hashtable
containsKey(Object) Returns 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() Returns 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

The Properties class is what enables end-users to customize their Java program. 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 it 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 loads 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 program Properties1 in Listing 11.11 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 11.11. Properties1.java: A sample Properties program.

import java.io.DataInputStream;
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 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 11.15 summarizes the methods of the Properties class.

Table 11.15. The variables and methods available in the Properties interface.

Variable Description
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 all the keys
save(OutputStream, String) Writes the properties to an OutputStream

The Observable Class

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 11.16 summarizes the complete interface of the Observable class.

Table 11.16. 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() Returns 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

Summary

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 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.

TOCBACKFORWARDHOME


©Copyright, Macmillan Computer Publishing. All rights reserved.