The
Legacy Classes and Interfaces
As explained at the start of
this chapter, early versions of java.util
did not include the Collections Framework. Instead, it defined several classes
and an interface that provided an ad hoc method of storing objects. When
collections were added (by J2SE 1.2), several of the original classes were
reengineered to support the collection interfaces. Thus, they are now
technically part of the Collections Framework. However, where a modern
collection duplicates the functionality of a legacy class, you will usually
want to use the newer collection class. In general, the legacy classes are
supported because there is still code that uses them.
One other point: none of the
modern collection classes described in this chapter are synchronized, but all
the legacy classes are synchronized. This distinction may be important in some
situations. Of course, you can easily synchronize collections by using one of
the algorithms provided by Collections.
The legacy classes defined by java.util are shown here:
There is one legacy interface called Enumeration. The following sections
examine Enumeration and each of the
legacy classes, in turn.
The
Enumeration Interface
The Enumeration interface defines the methods by which you can enumerate (obtain one at a time) the
elements in a collection of objects. This legacy interface has been superseded
by Iterator. Although not
deprecated, Enumeration is
considered obsolete for new code. However, it is used by several methods
defined by the legacy classes (such as Vector
and Properties) and is used by
several other API classes. Because it is still in use, it was retrofitted for generics by JDK 5. It has this
declaration:
interface
Enumeration<E>
where E specifies the type of element being enumerated. Enumeration specifies the following two
methods:
boolean hasMoreElements( ) E
nextElement( )
When implemented, hasMoreElements( ) must return true while there are still more
elements to extract, and false when
all the elements have been enumerated. nextElement(
) returns the next object in the enumeration. That is, each call to nextElement( ) obtains the next object
in the enumeration. It throws NoSuchElementException
when the enumeration is complete.
Vector
Vector implements a dynamic array. It is similar to ArrayList, but with two differences: Vector is synchronized,
and it contains many legacy methods that duplicate the functionality of methods
defined by the Collections Framework. With the advent of collections, Vector was reengineered to extend AbstractList and to implement the List interface. With the release of JDK
5, it was retrofitted for generics and reengineered to implement Iterable. This means that Vector is fully compatible with
collections, and a Vector can have
its contents iterated by the enhanced for
loop.
Vector is declared like this: class
Vector<E>
Here, E specifies the type of element that will be stored. Here are the Vector constructors:
Vector( ) Vector(int size)
Vector(int size, int incr) Vector(Collection<? extends E> c)
The first form creates a default vector, which
has an initial size of 10. The second form creates a vector whose initial
capacity is specified by size. The
third form creates a vector whose initial capacity is specified by size and whose increment is specified by
incr. The increment specifies the
number of elements to allocate each time that a vector is resized upward. The
fourth form creates a vector that contains the elements of collection c.
All vectors start with an
initial capacity. After this initial capacity is reached, the next time that
you attempt to store an object in the vector, the vector automatically
allocates space for that object plus extra room for additional objects. By
allocating more than just the required memory, the vector reduces the number of
allocations that must take place as the vector grows. This reduction is
important, because allocations are costly in terms of time. The amount of extra
space allocated during each reallocation is determined by the increment that
you specify when you create the vector. If you don’t specify an increment, the
vector’s size is doubled by each allocation cycle.
Vector defines these protected data members:
int capacityIncrement; int
elementCount; Object[ ] elementData;
The increment value is stored
in capacityIncrement. The number of
elements currently in the vector is stored in elementCount. The array that holds the vector is stored in elementData.
In addition to the
collections methods specified by List,
Vector defines several legacy
methods, which are summarized in Table 18-16.
Because Vector
implements List, you can use a
vector just like you use an ArrayList
instance. You can also manipulate one using its legacy methods. For example,
after you instantiate a Vector, you
can add an element to it by calling addElement(
). To obtain the element at a specific location, call elementAt( ). To obtain the first element in the vector, call firstElement( ). To retrieve the last
element, call lastElement( ). You
can obtain the index of an element by using indexOf( ) and lastIndexOf(
). To remove an element, call removeElement(
) or removeElementAt( ).
The following program uses a
vector to store various types of numeric objects. It demonstrates several of
the legacy methods defined by Vector.
It also demonstrates the
Enumeration interface.
// Demonstrate various Vector operations.
import java.util.*;
class VectorDemo {
public static void main(String args[]) {
// initial size is 3, increment is 2
Vector<Integer> v = new
Vector<Integer>(3, 2);
System.out.println("Initial size: " +
v.size());
System.out.println("Initial capacity:
" +
v.capacity());
v.addElement(1);
v.addElement(2);
v.addElement(3);
v.addElement(4);
System.out.println("Capacity after four
additions: " + v.capacity());
v.addElement(5);
System.out.println("Current capacity: " +
v.capacity());
v.addElement(6);
v.addElement(7);
System.out.println("Current capacity:
" + v.capacity());
v.addElement(9);
v.addElement(10);
System.out.println("Current capacity:
" + v.capacity());
v.addElement(11);
v.addElement(12);
System.out.println("First element: "
+ v.firstElement());
System.out.println("Last element: " +
v.lastElement());
if(v.contains(3))
System.out.println("Vector contains 3.");
// Enumerate the elements in the vector.
Enumeration<Integer> vEnum = v.elements();
System.out.println("\nElements in
vector:");
while(vEnum.hasMoreElements())
System.out.print(vEnum.nextElement() + "
"); System.out.println();
}
}
The output from this program
is shown here:
Initial size: 0
Initial capacity: 3
Capacity after four additions: 5
Current capacity: 5
Current capacity: 7
Current capacity: 9
First element: 1
Last element: 12
Vector contains 3.
Elements in vector:
1 2 3 4 5 6 7 9 10 11 12
Instead of relying on an
enumeration to cycle through the objects (as the preceding program does), you
can use an iterator. For example, the following iterator-based code can be
substituted into the program:
// Use an iterator to display contents.
Iterator<Integer> vItr = v.iterator();
System.out.println("\nElements in
vector:");
while(vItr.hasNext())
System.out.print(vItr.next() + " ");
System.out.println();
You can also use a for-each for loop to cycle through a Vector, as the following version of the
preceding code shows:
// Use an enhanced for loop to display contents
System.out.println("\nElements in
vector:"); for(int i : v)
System.out.print(i + " ");
System.out.println();
Because the Enumeration interface is not
recommended for new code, you will usually use an iterator or a for-each for loop to enumerate the contents of a
vector. Of course, legacy code will employ Enumeration.
Fortunately, enumerations and iterators work in nearly the same manner.
Stack
Stack is a subclass of Vector that
implements a standard last-in, first-out stack. Stack only defines the
default constructor, which creates an empty stack. With the release of JDK 5, Stack was retrofitted for generics and
is declared as shown here:
class Stack<E>
Here, E specifies the type of element stored in the stack.
Stack includes all the methods
defined by Vector and adds several
of its own, shown in Table 18-17.
Method : Description
boolean empty( ) : Returns true if the stack is
empty, and returns false if the stack contains elements.
E peek( ) : Returns the element on the top of
the stack, but does not remove it.
E pop( ) : Returns the element on the top of
the stack, removing it in the : process.
E push(E element) : Pushes element onto the
stack. element is also returned.
int search(Object element) : Searches for
element in the stack. If found, its offset from the top of the stack is
returned. Otherwise, –1 is returned.
Table 18-17 The Methods Defined by Stack
To put an object on the top of the stack, call push( ). To remove and return the top
element, call pop( ). You can use peek( ) to return, but not remove, the
top object. An EmptyStackException is
thrown if you call pop( ) or peek( ) when the invoking stack is empty. The empty( ) method returns true
if nothing is on the stack. The search(
) method determines whether an object exists on the stack and returns the
number of pops that are required to bring it to the top of the stack. Here is
an example that creates a stack, pushes several Integer objects onto it, and then pops them off again:
// Demonstrate the Stack class.
import java.util.*;
class StackDemo {
static void showpush(Stack<Integer> st,
int a) { st.push(a);
System.out.println("push(" + a +
")"); System.out.println("stack: " + st);
}
static void showpop(Stack<Integer> st) {
System.out.print("pop -> ");
Integer a = st.pop(); System.out.println(a);
System.out.println("stack: " + st);
}
public static void main(String args[]) {
Stack<Integer> st = new Stack<Integer>();
System.out.println("stack: " + st);
showpush(st, 42);
showpush(st, 66); showpush(st, 99);
showpop(st); showpop(st); showpop(st);
try { showpop(st);
} catch (EmptyStackException e) { System.out.println("empty
stack");
}
}
}
The following is the output
produced by the program; notice how the exception handler for EmptyStackException is caught so that
you can gracefully handle a stack underflow:
stack: [ ] push(42) stack: [42] push(66)
stack: [42, 66] push(99)
stack: [42, 66, 99] pop -> 99
stack: [42, 66] pop -> 66 stack: [42]
pop -> 42 stack: [ ]
pop -> empty stack
One other point: although Stack is not deprecated, ArrayDeque is a better choice.
Dictionary
Dictionary is an abstract class that represents a key/value storage repository
and operates much like Map. Given a key and value, you can
store the value in a Dictionary
object. Once the value is stored, you can retrieve it by using its key. Thus,
like a map, a dictionary can be thought of as a list of key/value pairs.
Although not currently deprecated, Dictionary
is classified as obsolete, because it is fully superseded by Map. However, Dictionary is still in use and thus is discussed here.
With the advent of JDK 5, Dictionary was made generic. It is
declared as shown here: class Dictionary<K, V>
Here, K specifies the type of keys, and V specifies the type of values. The abstract methods defined by Dictionary are listed in Table 18-18.
To add a key and a value, use the put( ) method. Use get( ) to retrieve the value of a given key. The keys and values
can each be returned as an Enumeration
by the keys( ) and elements( ) methods, respectively. The size( ) method returns the number of
key/value pairs stored in a
dictionary, and isEmpty( ) returns true when the dictionary is empty. You
can use the remove( ) method to
delete a key/value pair.
Method :
: Purpose
Enumeration<V> elements( ) : : Returns an enumeration of the values
contained in the dictionary.
V get(Object key) : : Returns the object that contains the value
associated with key. If key is not in the dictionary, a null object is
returned.
boolean isEmpty( ) : : Returns true if the dictionary is empty,
and returns false if it contains at least one key.
Enumeration<K> keys( ) : : Returns an enumeration of the keys
contained in the dictionary.
V put(K key, V value) : : Inserts a key and its value into the
dictionary. Returns null if key is not already in the dictionary; returns the
previous value associated with key if key is already in the dictionary.
V remove(Object key) : : Removes key and its value. Returns the
value associated with key. If key is not in the dictionary, a null is returned.
int size( ) :
: Returns the number of entries in the dictionary.
Table 18-18 The Abstract Methods Defined by Dictionary
Hashtable
Hashtable was part of the original
java.util and is a concrete implementation of a Dictionary. However, with the advent of collections, Hashtable was reengineered to also implement the Map interface. Thus, Hashtable
is integrated into the Collections Framework. It is similar to HashMap, but is synchronized.
Like HashMap, Hashtable
stores key/value pairs in a hash table. However, neither keys nor values can be
null. When using a Hashtable, you specify an object that
is used as a key, and the value that you want linked to that key. The key is
then hashed, and the resulting hash code is used as the index at which the
value is stored within the table.
Hashtable was made generic by JDK 5. It is declared like this:
class Hashtable<K, V>
Here, K specifies the type of keys, and V specifies the type of values.
A hash table can only store
objects that override the hashCode( )
and equals( ) methods that are
defined by Object. The hashCode( ) method must compute and
return the hash code for the object. Of course, equals( ) compares two objects. Fortunately, many of Java’s
built-in classes already implement the hashCode(
) method. For example, the most common type of Hashtable uses a String
object as the key. String implements
both hashCode( ) and equals( ).
The Hashtable constructors are shown here:
Hashtable( )
Hashtable(int size)
Hashtable(int size, float fillRatio)
Hashtable(Map<? extends K, ? extends V> m)
The first version is the
default constructor. The second version creates a hash table that has an
initial size specified by size. (The
default size is 11.) The third version creates a hash table that has an initial
size specified by size and a fill
ratio specified by fillRatio. This
ratio must be between 0.0 and 1.0, and it determines how full the hash table
can be before it is resized upward. Specifically, when the number of elements
is greater than the capacity of the hash table multiplied by its fill ratio,
the hash table is expanded. If you do not specify a fill ratio, then 0.75 is
used. Finally, the fourth version creates a hash table that is initialized with
the elements in m. The default load
factor of 0.75 is used.
In addition to the methods defined by the Map interface, which Hashtable now implements, Hashtable defines the legacy methods
listed in Table 18-19. Several methods throw NullPointerException if an attempt is made to use a null key or value.
Method : Description
void clear( ) : Resets and empties the hash
table.
Object clone( ) : Returns a duplicate of the
invoking object.
boolean contains(Object value) : Returns true
if some value equal to value exists within the hash table. Returns false if the
value isn’t found.
boolean containsKey(Object key) : Returns true
if some key equal to key exists within the hash table. Returns false if the key
isn’t found.
boolean containsValue(Object value) : Returns
true if some value equal to value exists within the hash table. Returns false if
the value isn’t found.
Enumeration<V> elements( ) : Returns an
enumeration of the values contained in the hash table.
V get(Object key) : Returns the object that
contains the value associated with key. If key is not in the hash table, a null
object is returned.
boolean isEmpty( ) : Returns true if the hash
table is empty; returns false if it contains at least one key.
Enumeration<K> keys( ) : Returns an
enumeration of the keys contained in the hash table.
V put(K key, V value) : Inserts a key and a
value into the hash table. Returns null if key isn’t already in the hash table;
returns the previous value associated with key if key is already in the hash
table.
void rehash( ) : Increases the size of the hash
table and rehashes all of its keys.
V remove(Object key) : Removes key and its
value. Returns the value associated with key. If key is not in the hash table,
a null object is returned.
int size( ) : Returns the number of entries in
the hash table. :
String toString( ) : Returns the string
equivalent of a hash table.
Table 18-19 The Legacy Methods Defined by Hashtable
The following example reworks
the bank account program, shown earlier, so that it uses a Hashtable to store the names of bank depositors and their current
balances:
// Demonstrate a Hashtable.
import java.util.*;
class HTDemo {
public static void main(String args[]) {
Hashtable<String, Double> balance =
new Hashtable<String, Double>();
Enumeration<String> names; String str;
double bal;
balance.put("John Doe", 3434.34);
balance.put("Tom Smith", 123.22);
balance.put("Jane Baker", 1378.00);
balance.put("Tod Hall", 99.22);
balance.put("Ralph Smith", -19.08);
// Show all balances in hashtable.
names = balance.keys(); while(names.hasMoreElements())
{
str = names.nextElement();
System.out.println(str + ": " +
balance.get(str));
}
System.out.println();
// Deposit 1,000 into John Doe's account.
bal = balance.get("John Doe");
balance.put("John Doe", bal+1000);
System.out.println("John Doe's new
balance: " + balance.get("John Doe"));
}
}
The output from this program
is shown here:
Todd Hall: 99.22
Ralph Smith: -19.08
John Doe: 3434.34
Jane Baker: 1378.0
Tom Smith: 123.22
John Doe's new balance: 4434.34
One important point: Like the
map classes, Hashtable does not
directly support iterators. Thus, the preceding program uses an enumeration to
display the contents of balance.
However, you can obtain set-views of the hash table, which permits the use of iterators. To do so, you simply use
one of the collection-view methods defined by Map, such as entrySet( )
or keySet( ). For example, you can
obtain a set-view of the keys and cycle through them using either an iterator
or an enhanced for loop. Here is a
reworked version of the program that shows this technique:
// Use iterators with a Hashtable.
import java.util.*;
class HTDemo2 {
public static void main(String args[]) {
Hashtable<String, Double> balance =
new Hashtable<String, Double>();
String str; double bal;
balance.put("John Doe", 3434.34);
balance.put("Tom Smith", 123.22);
balance.put("Jane Baker", 1378.00);
balance.put("Tod Hall", 99.22);
balance.put("Ralph Smith", -19.08);
//Show all balances in hashtable.
//First, get a set view of the keys.
Set<String> set = balance.keySet();
//Get an iterator.
Iterator<String> itr = set.iterator();
while(itr.hasNext()) {
str = itr.next(); System.out.println(str +
": " +
balance.get(str));
}
System.out.println();
// Deposit 1,000 into John Doe's account.
bal = balance.get("John Doe");
balance.put("John Doe", bal+1000);
System.out.println("John Doe's new
balance: " + balance.get("John Doe"));
}
}
Properties
Properties is a subclass of Hashtable.
It is used to maintain lists of values in which the key is a String and the
value is also a String. The Properties class is used by some other
Java classes. For example, it is the type of object returned by System.getProperties( ) when obtaining
environmental values. Although the Properties
class, itself, is not generic, several of its methods are.
Properties defines the following instance variable:
Properties defaults;
This variable holds a default
property list associated with a Properties
object. Properties defines these
constructors:
Properties( )
Properties(Properties propDefault)
The first version creates a Properties object that has no default
values. The second creates an object that uses propDefault for its default values. In both cases, the property
list is empty.
In addition to the methods
that Properties inherits from Hashtable, Properties defines the methods listed in Table 18-20. Properties also contains one deprecated
method: save( ). This was replaced
by store( ) because save( ) did not handle errors
correctly.
One useful capability of the Properties class is that you can
specify a default property that will be returned if no value is associated with
a certain key. For example, a default value can be specified along with the key
in the getProperty( ) method—such as
getProperty( "name"
,"default value"). If the "name" value is not found,
then "default value" is
returned. When you construct a Properties
object, you can pass another instance of Properties
to be used as the default properties for the new instance. In this case, if you
call getProperty("foo") on
a given Properties object, and
"foo" does not exist, Java looks for "foo" in the default Properties object. This allows for
arbitrary nesting of levels of default properties.
The following example
demonstrates Properties. It creates
a property list in which the keys are the names of states and the values are
the names of their capitals. Notice that the attempt to find the capital for
Florida includes a default value.
// Demonstrate a Property list.
import java.util.*;
class PropDemo {
public static void main(String args[]) {
Properties capitals = new Properties();
capitals.put("Illinois",
"Springfield");
capitals.put("Missouri",
"Jefferson City");
capitals.put("Washington",
"Olympia");
capitals.put("California",
"Sacramento");
capitals.put("Indiana",
"Indianapolis");
//Get a set-view of the keys.
Set<?> states = capitals.keySet();
//Show all of the states and capitals.
for(Object name : states)
System.out.println("The capital of "
+
name + " is " +
capitals.getProperty((String)name) + ".");
System.out.println();
// Look for state not in list -- specify
default.
String str =
capitals.getProperty("Florida", "Not Found"); System.out.println("The
capital of Florida is " + str + ".");
}
}
The output from this program
is shown here:
The capital of Missouri is Jefferson City.
The capital of Illinois is Springfield.
The capital of Indiana is Indianapolis.
The capital of California is Sacramento.
The capital of Washington is Olympia.
The capital of Florida is Not Found.
Since Florida is not in the
list, the default value is used.
Although it is perfectly
valid to use a default value when you call getProperty(
), as the preceding example shows, there is a better way of handling
default values for most applications of property lists. For greater
flexibility, specify a default property list when constructing a Properties object. The default list
will be searched if the desired key is not found in the main list. For example,
the following is a slightly reworked version of the preceding program, with a
default list of states specified. Now, when Florida is sought,
it will be found in the
default list:
// Use a default property list.
import java.util.*;
class PropDemoDef {
public static void main(String args[]) {
Properties defList = new Properties(); defList.put("Florida",
"Tallahassee"); defList.put("Wisconsin",
"Madison");
Properties capitals = new Properties(defList);
capitals.put("Illinois",
"Springfield"); capitals.put("Missouri", "Jefferson
City"); capitals.put("Washington", "Olympia");
capitals.put("California", "Sacramento"); capitals.put("Indiana",
"Indianapolis");
//Get a set-view of the keys.
Set<?> states = capitals.keySet();
//Show all of the states and capitals.
for(Object name : states)
System.out.println("The capital of "
+
name + " is " +
capitals.getProperty((String)name) + ".");
System.out.println();
// Florida will now be found in the default
list.
String str =
capitals.getProperty("Florida");
System.out.println("The capital of Florida
is "
+ str + ".");
}
}
Using
store( ) and load( )
One of the most useful
aspects of Properties is that the
information contained in a Properties object
can be easily stored to or loaded from disk with the store( ) and load( ) methods.
At any time, you can write a Properties
object to a stream or read it back. This makes property lists especially convenient
for implementing simple databases. For example, the following program uses a
property list to create a simple computerized telephone book that stores names
and phone numbers. To find a person’s number, you enter his or her name. The
program uses the store( ) and load( ) methods to store and retrieve
the list. When the program executes, it first tries to load the list from a
file called phonebook.dat. If this
file exists, the list is loaded. You can then add to the list. If you do, the
new list is saved when you terminate the program. Notice how little code is
required to implement a small, but functional, computerized phone book.
/* A simple telephone number database that uses a
property list. */
import java.io.*;
import java.util.*;
class Phonebook {
public static void main(String args[]) throws
IOException
{
Properties ht = new Properties();
BufferedReader br =
new BufferedReader(new
InputStreamReader(System.in)); String name, number;
FileInputStream fin = null; boolean changed =
false;
// Try to open phonebook.dat file.
try {
fin = new
FileInputStream("phonebook.dat");
} catch(FileNotFoundException e) { // ignore
missing file
}
/* If phonebook file already exists, load
existing telephone numbers. */
try {
if(fin != null) { ht.load(fin); fin.close();
}
} catch(IOException e) {
System.out.println("Error reading file.");
}
// Let user enter new names and numbers.
do {
System.out.println("Enter new name" +
" ('quit' to stop): ");
name = br.readLine();
if(name.equals("quit")) continue;
System.out.println("Enter number: ");
number = br.readLine();
ht.put(name, number); changed = true;
} while(!name.equals("quit"));
// If phone book data has changed, save it.
if(changed) {
FileOutputStream fout = new
FileOutputStream("phonebook.dat");
ht.store(fout, "Telephone Book");
fout.close();
}
// Look up numbers given a name.
do {
System.out.println("Enter name to
find" + " ('quit' to quit): ");
name = br.readLine();
if(name.equals("quit")) continue;
number = (String) ht.get(name);
System.out.println(number);
} while(!name.equals("quit"));
}
}
Related Topics
Privacy Policy, Terms and Conditions, DMCA Policy and Compliant
Copyright © 2018-2026 BrainKart.com; All Rights Reserved. Developed by Therithal info, Chennai.