// 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 * * 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 + " >"; } } }