| Closed Lists and Related Data Structures closedLists |
| Open vs. Closed Lists |
| Two list models are described in the text: | ||||
| Open lists: | ||||
| Elements and sublists can be shared | ||||
| Mutation of lists is discouraged | ||||
| Mathematically elegant | ||||
| Closed lists: | ||||
| Sharing generally not done | ||||
| Mutation of lists is ok, because they are encapsulated | ||||
| Mathematically less attractive | ||||
| Closed lists can be built by wrapping open lists | ||||
| Purpose of Closed Lists |
| A closed list is used for its identity as an object, rather than purely for its value as a sequence. | |
| Several ÒclientsÓ can access the same closed list; modifications made by one will be seen by all. | |
| In some cases, this is the desired behavior. | |
| In some cases, more space-efficient due to in-place modification. | |
| Closed List Implementation |
| A closed list can be implemented as an Òopen list in a boxÓ. | |
| Cells in the list are typically not shared from the outside, so they can be mutated at will. | |
| Outside access is through a mutable reference called the ÒheadÓ. |
| An Empty Closed List |
| Possible Java implementation |
| How to Add and Remove Elements? |
| To add, must specify: | ||
| Item to be added | ||
| To where it should be added | ||
| To remove, must specify | ||
| From where it should be removed | ||
| Some Typical Choices |
| Always add and remove from the head. | |
| Always add and remove from the tail. | |
| Add to the tail, remove from the head. | |
| Add and remove at random places (how to identify where?) |
| Common Closed List Usages |
| Stack | ||
| remembers elements in reverse order of entry, i.e. last-in element is first-out (ÒLIFOÓ) | ||
| Queue | ||
| remembers elements in order of entry, i.e. first-in element is first-out (ÒFIFOÓ) | ||
| Stack Abstraction |
| Stack s = new Stack(); | |
| s.push(ÒaÓ); | |
| s.push(ÒbÓ); | |
| s.push(ÒcÓ); | |
| value = s.pop(); // value will be ÒcÓ | |
| value = s.pop(); // value will be ÒbÓ | |
| value = s.pop(); // value will be ÒaÓ |
| Stack Implementation (push) |
| Stack Implementation (pop) |
| Reading Code containing References and Pointers |
| Suppose s and t are references. | |
| Read the assignment statement s = t; as Òmake s point to where t pointsÓ. |
|
| To see why, consider |
| Figurative Code for Push/Pop |
| s.push(Object x): s.head = new Cell(x, s.head); |
|
| s.pop(): Object top = s.head.value; s.head = s.head.next; return top; |
| Push -> Shove |
| Define shove to be an operation that adds the contents of an entire open list to a stack, with the first item in the list being the last item to be added. | |
| How could this be coded? |
| Queue Abstraction |
| Queue r = new Queue(); | |
| r.enqueue(ÒaÓ); | |
| r.enqueue(ÒbÓ); | |
| r.enqueue(ÒcÓ); | |
| value = r.dequeue(); // value will be ÒaÓ | |
| value = r.dequeue(); // value will be ÒbÓ | |
| value = r.dequeue(); // value will be ÒcÓ |
| Queue Implementation |
| For a queue, we usually add another reference, to the last element, for convenience. This element is called the tail. |
| Enqueue/Dequeue |
| enqueue adds a new element to one end of the internal open list. | |
| dequeue removes an element and returns it. | |
| But which end is used for which? |
| Homework |
| Write the code for enqueue and dequeue. |
| Related Topics |
| Lists of lists: No problem with OpenList, or in any framework in which lists contain Objects and are objects. | |
| Otherwise, need to define special type of list, tailored to the type of element being listed. |
| Doubly-Linked Lists |
| An implementation concept | ||
| Could use to implement double-ended queues: | ||
| ÒdequesÓ (pron. ÒdeckÓ) | ||
| not to be confused with ÒdequeueÓ | ||
| Deque Abstraction |
| void pushFront(Object) | |
| Object popFront() | |
| void pushBack(Object) | |
| Object popBack() | |
| boolean isEmpty() | |
| General Doubly-Linked Lists |
| Extend usage in Deque by allowing insertion and removal at arbitrary points | ||
| Can access the object before any object, as well as after, unlike singly-linked lists. | ||
| Disadvantages: | ||
| More storage is used for the extra pointer per cell. | ||
| Sharing is extremely tricky; better not done. | ||
| Applications? | ||
| Doubly-Linked Lists as an Implementation Concept |
| In the implementation (as opposed to an appropriate abstraction), we realize that the list is composed of cells. | |
| Cells make it easy to talk about various operations |
| Doubly-Linked Lists as an Implementation Concept |
| Cells make it easy to talk about various operations: | ||
| void insertAfter(Cell, newCell) | ||
| void insertBefore(Cell, newCell) | ||
| void remove(Cell) | ||
| Cell getNext() | ||
| Cell getPrevious() | ||
| Possible Abstractions for
Doubly-Linked Lists |
| A problem is that Cell, an implementation concept, does not make an attractive abstraction. | |
| A preferable view is to think in terms of a list Iterator (or Cursor), which maintains a position within a list and can move backward or forward. | |
| The Iterator determines an insertion point for a new value, or point before/after a value is removed. |
| Example: ListIterator (in java.util) |
| If L is a List, then L.listIterator() returns a ListIterator positioned at the start of the list. | ||
| For a ListIterator: | ||
| Object next() returns the next element, if any | ||
| boolean hasNext() tells whether there is a next element | ||
| Object previous() returns the previous element, if any | ||
| boolean hasPrevious() tells whether there is a previous element | ||
| void set(Object) sets the value at the current position | ||
| void remove() removes the value at the current position | ||
| void add(Object) adds a value at the current position | ||
| Example, part 1 (complete source file) |
| Example (contÕd) |
| Example (contÕd) |
| Iterators as Interfaces |
| Review of Interfaces in Java |
| A principal abstraction mechanism in Java is the formal concept of interface. | |
| An interface is like a class, except that it only declares methods, it does not implement them. | |
| A given class may implement the interface by giving concrete methods for each of the ones declared in the interface. | |
| The class definition must assert that it implements the interface. The compiler checks that this is the case. |
| Interface vs. Implementation |
| Use of an Iterator |
| Recall Important Aspects of Interface Concept |
| An interface is a type, just as a class is. | |
| When a variableÕs type is that of an interface, a variable of any implementing class type may be used. | |
| Any number of distinct classes can implement a given interface. | |
| Implication of second point |
| Reemphasis: Power of Interface |
| The interface abstraction is powerful, because it does not require the user to know which implementation is being used. | |
| The user of a method that specifies an interface argument can thus pass an object of any class that implements the interface. |
| Value of Interfaces |
| Interfaces force the provider of a service to give a clear specification of what the service is, independent of how the service is implemented. |
| Enumerations |
| An Enumeration is the name given, in early Java, for a special one-way Iterator. | |
| hasMoreElements() is used instead of hasNext() | |
| nextElement() is used instead of next() | |
| no remove() is specified | |
| Typically, an enumeration is obtained from an Object by the elements() method. |
| Example: Enumerating a Vector |
| More Interface Examples with some Implementations (see Java API) |
| List | ||
| Interface: List | ||
| Implementations: LinkedList, Vector, ArrayList | ||
| Set | ||
| Interface: Set | ||
| Implementations: HashSet, TreeSet | ||
| Comparable interface | ||
| Character, Double, Long, String, BigInteger, Date, etc. | ||
| More Interface Examples with some Implementations |
| ListIterator interface | ||
| listIterator() returned by a LinkedList | ||
| Enumeration interface: read-only version of Iterator | ||
| elements() returned by a Vector, HashTable, or OpenList | ||
| StringTokenizer | ||
| Cloneable interface | ||
| Many classes | ||