Home | | Object Oriented Programming | | The Complete Reference Java | | Internet Programming | | Web Programming | The Legacy Classes and Interfaces - java.util

Chapter: Java The Complete Reference - The Java Library - java.util : The Collections Framework

| Study Material, Lecturing Notes, Assignment, Reference, Wiki description explanation, brief detail |

The Legacy Classes and Interfaces - java.util

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.

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"));

 

}

 

}

Study Material, Lecturing Notes, Assignment, Reference, Wiki description explanation, brief detail


Copyright © 2018-2020 BrainKart.com; All Rights Reserved. Developed by Therithal info, Chennai.