// 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 * <ul> * <li>Empty, or</li> * <li>An object (the list's head) followed by another list (the list's * tail)</li> * </ul> * 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. * <p>This class was created to support the text <cite>Algorithms & Data Structures: * The Science of Computing</cite> 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.</p> */ public class List implements Serializable { private Object head; private List tail; //constructors /** * Initialize a list to be empty. For example * <p><code>List things = new List();</code></p> */ public List() { head = null; tail = null; } /** * Initialize a List to contain one element. For example * <p><code>List words = new List( "lion" );</code></p> * @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 <code>List</code>. Every subclass of <code>List</code> * 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 <code>makeNewList</code> message */ public List makeNewList() { return new List(); } // Basic public functions /** * Extracts the head of a list. The list itself is left unchanged. * For example * <p><code>Object obj = myList.getFirst();</code></p> * @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 * <p><code>List theRest = myList.getRest();</code></p> * @return The tail of the list. */ public List getRest() { return this.tail; } /** * Determines whether a list is empty or not. For example * <p><code>if ( myList.isEmpty() ) ...</code></p> * @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 * <p><code>myList.removeItem();</code></p> */ public void removeItem() { this.head = this.tail.head; this.tail = this.tail.tail; } /** * Removes a list's first item and returns it. For example * <p><code>Object oldHead = myList.getAndRemove();</code></p> * 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 * <p><code>words.addItem( "wombat" );</code></p> * @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 * <p><code>words.setFirstItem( "aardvark" );</code></p> * @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 * <p><code>myList.setRest( new List() );</code></p> * 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 <code>concat</code> message. * For example * <p><code>myList.concat( new List("more") );</code></p> * @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 * <p><code>myList.printList();</code></p> * 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 * <p><code>myList.printListForward();</code></p> * 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 <code>equals</code> to compare * objects. For example * <p><code>if ( words.find("capybara") ) ...</code></p> * @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 <code>equals</code> * to compare the object to elements of the list. For example * <p><code>words.delete("monkey");</code></p> * @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 * <p><code>List savedList = myList.copy();</code></p> * @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 * <p><code>myList.save( "ListFile" );</code></p> * @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 * <p><code>myList.restore( "ListFile" );</code></p> * 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 * <p><code>String text = myList.toString();</code></p> * @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 + " >"; } } }