// The Science of Computing List class.
// Copyright (C) 2004. Charles River Media. All rights reserved.
// History:
// August 2003 -- Created by Greg Scragg.
// October 2003 -- updates to several methods by GWS
// February 2004 -- updated references and variable names to correspond to text changes
// March 2004 -- Minor code cleanup and correction of typos by Doug Baldwin.
package geneseo.cs.sc;
import java.io.*;
/**
* Represents lists of arbitrary objects. This class reflects a recursive
* definition of "list", specifically a definition that says that a list is either
*
* - Empty, or
* - An object (the list's head) followed by another list (the list's
* tail)
*
* These lists handle the common messages needed for any list class. Some
* messages are destructive (i.e., cause a list to change itself), and therefore
* do not require assignment within the client code when modifying lists.
* This class was created to support the text Algorithms & Data Structures:
* The Science of Computing by Doug Baldwin and Greg Scragg. All references herein
* to "the text" refer to that book. This class is the one described in
* Chapter 11, with a few extensions and with some methods completed that were "left
* to an exercise" in the text.
*/
public class List implements Serializable {
private Object head;
private List tail;
//constructors
/**
* Initialize a list to be empty. For example
* List things = new List();
*/
public List() {
head = null;
tail = null;
}
/**
* Initialize a List to contain one element. For example
* List words = new List( "lion" );
* @param newItem The single item the List will initially contain.
*/
public List(Object newItem) {
head = newItem;
tail = makeNewList();
}
/**
* Create a new list that is an instance of the same class as this list.
* This message is used, for example, when adding an item to a list requires
* creating a new tail for that list. The new tail should be an instance of
* the same class as the list itself, even if that list is an instance of
* a subclass of List
. Every subclass of List
* must therefore provide a method for handling this message that returns
* a new instance of that subclass. Clients are unlikely to ever invoke
* this method directly, but it is essential to the correct functioning of
* other methods defined for lists.
* @return A new, empty, list, of the same class as the one that received
* the makeNewList
message
*/
public List makeNewList() {
return new List();
}
// Basic public functions
/**
* Extracts the head of a list. The list itself is left unchanged.
* For example
* Object obj = myList.getFirst();
* @return The head of the list.
*/
public Object getFirst() {
return this.head;
}
/**
* Extracts the tail of a list. The list itself is left unchanged.
* For example
* List theRest = myList.getRest();
* @return The tail of the list.
*/
public List getRest() {
return this.tail;
}
/**
* Determines whether a list is empty or not. For example
* if ( myList.isEmpty() ) ...
* @return True if the list is empty; false otherwise.
*/
public boolean isEmpty() {
return (this.head == null && this.tail == null);
}
/**
* Removes a list's head. This alters the list, but does not return
* the item removed. For example
* myList.removeItem();
*/
public void removeItem() {
this.head = this.tail.head;
this.tail = this.tail.tail;
}
/**
* Removes a list's first item and returns it. For example
* Object oldHead = myList.getAndRemove();
* Note that this is both a value and side effect producing operation.
* @return The item removed from the list (which was the list's head).
*/
public Object getAndRemove() {
Object temp = this.head;
this.head = this.tail.head;
this.tail = this.tail.tail;
return temp;
}
/**
* Adds an object to a list. The object becomes the new head of the list,
* while all objects previously in the list effectively move one place
* down it. For example
* words.addItem( "wombat" );
* @param newItem The object to be added to the list.
*/
public void addItem(Object newItem) {
List temp = makeNewList();
temp.head = this.head;
temp.tail = this.tail;
this.head = newItem;
this.tail = temp;
}
/**
* Replaces the head of a list with a new object. For example
* words.setFirstItem( "aardvark" );
* @param value The object that will become the new head of the list.
*/
protected void setFirstItem(Object value) {
this.head = value;
}
/**
* Replaces the tail of a list with a new list. For example
* myList.setRest( new List() );
* Note that the replacement must be an entire list, not just a new element.
* @param newTail The new tail for the list.
*/
protected void setRest (List newTail) {
this.tail = newTail;
}
// Standard traversal methods
/**
* Concatenates two lists. All the items in the parameter list are added to
* the end of the list that receives the concat
message.
* For example
* myList.concat( new List("more") );
* @param extraList The list to be added to the end of the current list.
*/
public void concat(List extraList) {
if ( this.isEmpty() ) {
this.head = extraList.head;
this.tail = extraList.tail;
}
else if(this.tail.isEmpty())
this.tail = extraList;
else this.tail.concat(extraList);
}
/**
* Print the elements in a list from first to last. Prints elements from the
* front (head) of the list to the back, which, in effect, prints the last
* element entered first. For example
* myList.printList();
* Since this is intended for student use, it provides a marker at the end of the
* list to aid with testing and debugging.
*/
public void printList() {
if (!this.isEmpty()) {
System.out.println(this.getFirst());
this.getRest().printList();
}
else System.out.println("--");
}
/**
* Print the elements in a list from last to first. Prints elements from the
* back of the list to front (head), which, in effect, prints the first
* element entered first. For example
* myList.printListForward();
* Since this is intended for student use, it provides a marker at the end of the
* list to aid with testing and debugging.
*/
public void printListForward() {
if (!this.isEmpty()) {
this.getRest().printListForward();
System.out.println(this.getFirst());
}
else System.out.println("--");
}
/**
* Search a list for an object. Uses equals
to compare
* objects. For example
* if ( words.find("capybara") ) ...
* @param target The object to look for in the list.
* @return True if one of the elements of the list is equal to the
* target, false otherwise.
*/
public boolean find(Object target) {
if (this.isEmpty())
return false;
else if(this.getFirst().equals(target))
return true;
else return this.getRest().find(target);
}
/**
* Remove an object from a list. This removes the first occurrence of the
* requested object, altering the list in doing so. Uses equals
* to compare the object to elements of the list. For example
* words.delete("monkey");
* @param value The object to remove from the list.
*/
public void delete(Object value) {
if(!this.isEmpty())
if(this.getFirst().equals(value))
this.removeItem();
else this.getRest().delete(value);
}
/**
* Create a duplicate of a list. For example
* List savedList = myList.copy();
* @return The duplicate of the list.
*/
public List copy() {
if(this.isEmpty())
return new List();
else {
List temp = this.getRest().copy();
temp.addItem(this.getFirst());
return (temp);
}
}
/**
* Write a List to a file, replacing any previous data in that file.
* For example
* myList.save( "ListFile" );
* @param fileName The name of the file to write the list to.
*/
// This method opens a stream to the file, writes this list to the stream, and
// closes the stream. If anything goes wrong while doing these things, this method
// prints an error message but doesn't signal its client that anything has failed.
public void save( String fileName ) {
try {
ObjectOutputStream out = new ObjectOutputStream( new FileOutputStream( fileName ) );
out.writeObject( this );
out.close();
}
catch ( Exception error ) {
System.err.println( "Unable to save list: " + error );
}
}
/**
* Restore a list from a file. For example
* myList.restore( "ListFile" );
* After a list handles this message, the list's contents are
* replaced by the contents of the file, if possible. If for some
* reason the file couldn't be read, the contents of the list are
* unchanged.
* @param fileName The name of the file to restore the list from.
*/
// This opens an input stream and reads a list from that stream. If the read succeeds,
// this then copies the new lists's members into the list executing this method, thus
// changing that list into the one read. If anything goes wrong in reading, this prints
// an error message, but doesn't let client code know there was an error.
public void restore( String fileName ) {
try {
ObjectInputStream in = new ObjectInputStream( new FileInputStream( fileName ) );
List retrieved = (List) in.readObject();
in.close();
head = retrieved.head;
tail = retrieved.tail;
}
catch ( Exception error ) {
System.err.println( "Unable to restore a list: " + error );
}
}
/**
* Generate a string representation of a list. For example
* String text = myList.toString();
* @return The string representation of the list.
*/
// This generates the string recursively, using the insight that an empty
// list can be represented by the string "< >", while a non-empty
// one can be represented by the head, and the tail
// enclosed in "<" and ">".
public String toString() {
if ( this.isEmpty() ) {
return "<>";
}
else {
return "<" + head + " " + tail + " >";
}
}
}