• No results found

We may command the queue to makeEmpty and we may query the queue to tell us if it isEmpty or return its

Containers As Abstract Data Types

3. We may command the queue to makeEmpty and we may query the queue to tell us if it isEmpty or return its

size.

The Queue interface is an extension of Container so it inherits all methods in Container and adds only the new commands add and remove plus the query front as shown in Listing 10.3.

Listing 10.3 Interface Queue

/** Interface Queue */

public interface Queue extends Container {

// Commands

/** Add an object at the rear of the queue */

public void add (Object obj);

/** Remove an object from the front of the queue * Throws NoSuchElementException if queue is empty */

public void remove ();

/** Return without removing, the front object in the queue * Throws NoSuchElementException if queue is empty */

public Object front ();

}

10.3—

Supporting Interface and Classes

Before continuing our presentation of the hierarchy of container abstractions (interfaces), we discuss two important supporting abstractions: Comparable and Association. These abstractions are used to support some of the containers that are defined in the following sections.

Ordered containers contain elements whose position within the container is based on the magnitude of some feature of the contained objects. More precisely, the objects in an ordered container must be comparable. Requiring the class of the object to implement interface Comparable enforces this property. The details of interface Comparable are shown in Listing 10.4, abstracted from the Java source file for Comparable in package java.lang. Comparable contains a single query, compareTo.

The compareTo query returns an int whose value depends on the relative magnitudes of the object receiving the message (receiver) and the parameter object (obj). It returns -1 if the receiver is less than obj, 0 if the receiver is equal to obj, and 1 if the receiver is greater than obj.

Listing 10.4 Interface Comparable

/** Interface Comparable */

public interface Comparable {

// Queries

/** Return -1 if the receiver is less than obj, * 0 if the receiver equals obj and

* 1 if the receiver is greater than obj */

public int compareTo (Object obj);

}

Class Association allows us to group a key with a value. There is an association between a key and its value. Class

Association plays an important support role in our study of data structures where we need a container of associated key- value pairs.

Dictionaries contain instances of Association. Keys organize the dictionary; that is, we typically search for an object in the dictionary by looking up its key.

TEAM

FLY

Lookup requires a test for equality. If the dictionary is ordered (based on the relative magnitude of contained keys), then we must ensure that the keys in any associations entered into the OrderedDictionary also be Comparable. We also choose to require that associations be serializable to be consistent with our choice that all containers be serializable. Listing 10.5 gives the details for class Association. It is a regular class, not an interface.

Listing 10.5 Class Association

/** Class Association

* An instance must initialize a key on creation.

* If used as a comparable Association, keys must be comparable and * comparison is based on keys only.

* Note that equals() does not enforce the comparable feature and * requires equality of both key and value.

*/

package foundations;

import java.io.Serializable;

public class Association extends Object

implements Comparable, Serializable {

// Fields

private Object key; private Object value; // Constructors

/** Create an instance with specified key and null value */

public Association (Object key) {

this(key, null); }

/** Create an instance with specified key and value */

public Association (Object key, Object value) {

this.key = key; this.value = value; }

// Commands /** Set the value */

public void setValue (Object value) {

this.value = value; }

// Queries /** return key */

public Object key () {

return key; }

/** Return value */

public Object value () {

return value; }

/** Return a String representation.

* Return a String of the form <key:value> */

public String toString () {

return '' < " + key + ":" + value + " > " ; }

/** Override inherited Object method equals() */

public boolean equals (Object obj) {

if (obj instanceof Association)

return (key.equals(((Association)obj).key)

&& value.equals(((Association)obj).value)); else

return false; }

/** Implement Comparable method compareTo

* Compare based only on key; key must be Comparable */

public int compareTo (Object obj) {

return ((Comparable)key).compareTo(((Association)obj).key()); }

/** Override inherited Object method hashCode(). * Return a unique int representing this object */

public int hashCode () {

int bits1 = key.hashCode(); int bits2 = value.hashCode(); return (bits1 << 8)^(bits2 >> 8); }

10.4—

The Container Hierarchy

We next consider logic for partitioning our Container hierarchy to represent more specific kinds of containers. There are numerous potential criteria for creating a hierarchy of container abstractions. These criteria include ordered versus unordered, order based on container rules (e.g., position, index) versus contained object properties (e.g., relative magnitude), duplicates allowed or disallowed, or contained objects restricted to specific types (e.g., Comparable, Association instead of Object). In some cases the specific methods for a container are dependent on the internal structure of the container – for example, trees.

As with any good inheritance hierarchy, we are guided and constrained by the principle that methods defined in ancestor classes must make sense in all descendent classes.

We define additional interfaces that directly extend Container. They are List, BinaryTree, SearchTable, Dictionary, Heap, and Set. The distinguishing characteristics of each are given below. Figure 10.1 shows a class diagram for

Container and its direct descendant interfaces.

Figure 10.1.

List – A list has a linear structure. As a minimum requirement, we may access both ends of a list. Its contained objects have no particular order except as a result of the history of adding and removing objects at either end. The ends of the list may be characterized as front and rear. The list has some shared features with a queue; however, it has more. Variations on a list may be represented as extensions. These variations include indexable (allowing access at an index) and positionable (allowing access before or after a contained object). An ordered list (based on some property of the contained objects) is a special list that has fewer methods than List. It is implemented as a container whose methods enforce order (i.e., contained objects must be Comparable).

BinaryTree – A binary tree is a nonlinear structure consisting of binary nodes. Each node may have at most two offspring nodes (left and right). A tree is generally accessed at one location, a special node known as its root. A tree has commands and queries unique to its structure. There are several options for iteration over the objects in a tree. The objects in a tree may be ordered (represented by an implementation based on order of its contained objects) or not. An ordered binary tree (called a BinarySearchTree) is implemented as a SearchTable that enforces order based on properties of its contained elements (its elements must be Comparable).

SearchTable – The elements of this container must implement Comparable. An OrderedDictionary contains

associations and is a kind of SearchTable. A special queue called PriorityQueue is also a kind of SearchTable. Classes

BinarySearchTree and OrderedList implement SearchTable.

Dictionary – A dictionary contains key-value pairs (instances of Association). Commands and queries for a dictionary are centered on the keys. The keys must implement the equals query inherited from Object. A dictionary may also be ordered (on the keys). Interface OrderedDictionary requires that its keys be comparable. Thus OrderedDictionary

extends interface SearchTable (instead of Dictionary), which enforces the comparable property. This design choice was not easily made. An OrderedDictionary is a kind of Dictionary (implying that it should extend interface Dictionary). However, Java provides no mechanism for enforcing a type constraint (e.g., forcing an Object to be Comparable) for parameters in redefined methods in a subclass. We choose the design that allows us to enforce comparability of keys for the elements of an ordered dictionary.

Set – A set is an unordered container with the constraint that no duplicate copies of contained objects are allowed. This is the only container, considered so far, that strictly disallows duplicates. A set includes methods applicable to

mathematical sets.

Heap – A heap is a special binary tree (called a complete binary tree) whose contained objects obey the heap-ordering property. The heap-ordering property states that the object contained in a node in the tree is smaller than the contents of any node in its left and right subtrees. No other ordering relationship is implied or required.

In the following sections we present additional logic for various interfaces extended from Container. Please note that methods inherited from Container or

other ancestor classes are not repeated in the listings. This allows us to focus on what is new in the subinterfaces and is consistent with inheritance rules of object orientation and of Java.

10.4.1—

The List Interface and Its Descendants

Interface List extends Container. It is a linear structure with access to its front and rear. It adds several new commands and queries. The details of interface List are given in Listing 10.6.

Listing 10.6 Interface List

/** Interface List */

package foundations; import java.util.*;

public interface List extends Container {

// Commands

/** Add obj at the front of the list. */

public void addFront (Object obj);

/** Add obj at the rear of the list */

public void addRear (Object obj);

/** Remove object from the front of the list if found. */

public void removeFront ();

/** Remove object from the rear of the list if found. */

public void removeRear ();

// Queries

/** Return without removing the front object in the list * Throw NoSuchElementException if list is empty */

public Object front ();

/** Return without removing the rear object in the list * Throw NoSuchElementException if list is empty */

/** return true if the list contains obj */

public boolean contains (Object obj);

/** return an iterator on the elements in the list */

public Iterator elements ();

}

Notice that method elements in Listing 10.6 has a return type of Iterator. Iterators provide sequential access to the user of each element in a container using the messages shown in interface Iterator in Listing 10.7. The user may send appropriate messages to each contained object during the iteration. Iterators will be used in several of our container classes. They allow the user to access each element in the container. Listing 10.7 is abstracted from Iterator.java in the Java package java.util.

Listing 10.7 Interface Iterator

/** Interface Iterator */

public interface Iterator {

// Commands

/** Remove the last element returned by next() * Use only once after a call to next() */

public void remove ();

// Queries

/** Return true if the container has an unvisited element */

public boolean hasNext ();

/** Return next element in the container * Use only after hasNext() returns true

* Throws NoSuchElementException if no more elements */

public Object next ();

}

Interface List has a number of potential subinterfaces representing specific kinds of lists. Included are IndexableList and

Figure 10.2. The Listinterface hierarchy.

Listing 10.8 gives details for interface IndexableList. An indexable list is a list whose elements may be accessed via an index. The index is of type int. This interface adds commands for adding and removing an object at a specified index and a query for accessing an object at a specified index without removing it.

Listing 10.8 Interface IndexableList

/** Interface IndexableList */

package foundations;

public interface IndexableList extends List {

// Commands

/** Replace object at index with obj

* Throws ArrayIndexOutOfBoundsException if index error */

/** Remove an object at specified index

* Throws ArrayIndexOutOfBoundsException if index error */

public void removeAt (int index);

// Queries

/** Return the object at index without removing

* Throws ArrayIndexOutOfBoundsException if index error */

public Object elementAt (int index);

}

Listing 10.9 gives details for interface PositionableList. A positionable list is a list whose elements may be accessed relative to an object in the list, such as before or after. It adds commands for adding and removing an object before or after a specified object. The interface adds queries for accessing, without removing, an object before or after a specified object in the list.

Listing 10.9 Interface PositionableList

/** Interface PositionableList

* Objects in PositionableList must override equals() from Object */

package foundations;

public interface PositionableList extends List {

// Commands

/** Insert obj after target object in the list

* Throw NoSuchElementException if target not in the list. */

public void addAfter (Object obj, Object target);

/** Insert obj before target object in the list

* Throw NoSuchElementException if target not in the list. */

public void addBefore (Object obj, Object target);

/** Delete object after target object in the list

* Throw NoSuchElementException if target not in the list. * Throw NoSuchElementException if target is last in the list. */

/** Delete object before target object in the list

* Throw NoSuchElementException if target not in the list. * Throw NoSuchElementException if target is first in the list. */

public void removeBefore (Object target);

// Queries

/** Return object after target object in the list

* Throw NoSuchElementException if target not in the list. * Throw NoSuchElementException if target is last in the list. */

public Object elementAfter (Object target);

/** Return object before target object in the list

* Throw NoSuchElementException if target not in the list. * Throw NoSuchElementException if target is first in the list. */

public Object elementBefore (Object target);

}

10.4.2—

The BinaryTree Interface

Listing 10.10 gives details for interface BinaryTree. A binary tree generally does not require that its contained objects be ordered. Objects in the binary tree must respond to the equals method inherited from Object. Interface BinaryTree adds no new commands because the actual commands needed depend on the kind of binary tree. Almost all binary trees have need for the five queries added by interface BinaryTree. These new queries allow the user to know the maximum level or average path length of the tree and to return three varieties of iterators. The iterators allow the user to traverse (visit every node of) the binary tree using preorder, in-order, or postorder traversal algorithms.

Listing 10.10 Interface Binary Tree

/** Interface Binary Tree

* Contained objects must override equals() from Object /

package foundations; import java.util.*;

public interface Binary Tree extends Container {

// Commands // Queries

Related documents