Title: Organizing list implementations
1Chapter 23
- Organizing list implementations
2This chapter discusses
- The notion of an iterator.
- The standard Java library interface Collection,
and some of its related classes.
3A library structure
- We have yet to discuss how all the lists that we
have implemented are related. - We could organize them like this
4A library structure (cont.)
- With this approach we encounter problems when we
try to extend the base class for other purposes,
such as adding new functionality. - This approach causes class explosion as each
circumstance must be implemented in each subclass.
5A library structure (cont.)
- The problem is that the class List is a
participant in two distinct hierarchies an
implementation hierarchy and a concrete
application hierarchy. - Solution Use composition rather than extension
to handle different implementation strategies.
6A library structure (cont.)
- Build a separate implementation hierarchy.
- Provide each list with an implementation as a
component. - This kind of structure is called a bridge.
7Implementing List with a bridge
- public abstract class List implements Cloneable
-
- public Object get (int i)
- return imp.get(i)
-
-
-
- public interface ListImplementation extends
Cloneable -
- Object get (int i)
-
8Implementing List with a bridge (cont.)
- The ListImplementation subclasses are now
concrete classes. - Each concrete ListImplementation subclass must
provide an appropriate definition of the method
get. - class BoundedList implements ListImplementation
-
- public Object get (int i)
- return elementsi
-
-
9Specifying the implementation
- A ListImplementation is created when a List is
created. - We make the client responsible for determining
which implementation to use when creating the
List. - This means that deciding which implementation to
use for a particular List is not done until
run-time.
10(No Transcript)
11(No Transcript)
12(No Transcript)
13Iterators
- Many of the methods used with List are linear.
e.g. indexOf, remove. - The problems of examining each element of a
container and of obtaining a handle on a
particular container element are very general. - An iterator is an object associated with a
particular container that provides a means of
sequentially accessing each element in the
container.
14Iterators (cont.)
- public interface Iterator extends Cloneable
- Iterator for accessing and traversing elements
of a container. - public void reset ()
- Initialize this Iterator to reference the first
item. - public void advance ()
- Advance this Iterator to the next item.
- require
- !this.done()
15Iterators (cont.)
- public boolean done ()
- No more items to traverse in the container.
- public Object get ()
- Container item this Iterator currently
references. - require
- !this.done()
- public boolean equals (Object obj)
- The specified Object is an Iterator of the same
class as this, and references the same relative
item of the same container.
16Iterators (cont.)
- Assuming c is a container of some sort and i an
iterator associated with it, we can access each
item in the container as follows - i.reset()
- while (!i.done())
- do something with i.get(i)
- i.advance()
17Iterators (cont.)
- An int variable used to index a list is a simple
form of iterator. - It is reset by assigning a value of 0, and
advanced by incrementing. - The iterator is done when it equals the length of
the list. - For two Iterators to be equal, they must
reference the same relative item of the same
container. - Even if index 3 and 5 are referencing the same
object, an Iterator that references the item with
index 3 would not be equal to an Iterator that
references the item with index 5.
18Iterator classes
- If an interface is to traverse a container
efficiently, it must be linked closely to the
container implementation. - Therefore we create classes for each type of
container.
19ListIterator
- ListIterator implements an Iterator for arrays.
- When a ListIterator is created, it is given a
reference to the ListImplementation it will
traverse. - The current element referenced by the Iterator
is represented simply as an integer index.
20(No Transcript)
21(No Transcript)
22LinkedListIterator
- In a LinkedList, it is preferable to keep a
reference to a Node, which can be advanced
efficiently to reference the next Node in the
list. - Iterator must have access to the implementation
structure of the LinkedList. - A LinkedListIterator keeps a reference to the
Node containing the current item.
23(No Transcript)
24(No Transcript)
25Creating an Iterator
- Since an Iterator is intimately bound to the List
it is traversing, we add functionality for
creating an Iterator to the class List. - List forwards the responsibility to its
ListImplementation. - public abstract class List implements Cloneable
-
- /
- Create a new Iterator for this List.
- /
- public Iterator iterator ()
- return new imp.iterator()
-
-
26Creating an Iterator (cont.)
- ListImplementation defines a factory method that
produces the appropriate kind of Iterator. - interface ListImplementation extends Cloneable
-
- /
- Create a new Iterator for this List.
- /
- public Iterator iterator ()
-
27Creating an Iterator (cont.)
- Concrete implementation classes implement the
factory method to produce an appropriate
iterator. - class LinkedList extends ListImplementation
-
- /
- Create a new Iterator for this List.
- /
- public Iterator iterator ()
- return new LinkedListIterator(this)
-
-
-
28Creating an Iterator (cont.)
29Creating an Iterator (cont.)
30Creating an Iterator (cont.)
31List methods with iterators as arguments
- We overload the List methods that take index
arguments with methods taking iterators as
arguments.
32List methods with iterators as arguments (cont.)
- public Object get (Iterator i)
- The element referenced by the specified
Iterator. - require
- this.traversedBy(i)
- !i.done()
- public Iterator iteratorAt (Object obj)
- An Iterator referencing the first occurrence of
the specified element in this List Iterator is
done if this List does not contain the specified
element. - require
- obj ! null
- ensure
- if obj equals no element of this List then
- iteratorAt(obj).done()
- else
- obj.equals(iteratorAt(obj).get()), and
iteratorAt(obj) references the first position
in a tarversal for which this is true.
33List methods with iterators as arguments (cont.)
- public void add (Iterator i, Object obj)
- Insert the specified Object at the specified
position. - require
- this.traversedBy(i)
- !i.done()
- obj ! null
- ensure
- this.size() old.size() 1
- i.get() obj
- (i.advance() i.get()) old.i.get()
- public void remove (Iterator i)
- Remove the element at the specified position.
- require
- this.traversedBy(i)
- !i.done()
- ensure
- this.size() old.size() - 1
- i.get() (old.i.advance() old.i.get())
34List methods with iterators as arguments (cont.)
- public void set (Iterator i, Object obj)
- Replace the element at the specified position
with the specified Object. - require
- this.traversedBy(i)
- !i.done()
- public boolean traversedBy (Iterator i)
- This List is traversed by the specified Iterator.
35List methods with iterators as arguments (cont.)
- The final postconditions for add and remove
indicate a sequene of method calls. - The add postcondition (i.advance() i.get())
old.i.get() indicates that advancing the iterator
followed by a get gives the same element get
would have returned before the add. - The remove postcondition i.get()
(old.i.advance() old.i.get()) indicates that the
element referenced by the iterator after the
remove is the one following the element that was
removed.
36List methods with iterators as arguments (cont.)
- The implementation of these methods is
straightforward. - public Object get (Iterator i)
- Require.condition(this.traversedBy(i))
- return i.get()
-
- Most other operations are forwarded to the
ListImplementation. - public void remove (Iterator i)
- Require.condition(this.traversedBy(i))
- imp.remove(i)
37Improving LinkedListIterator
- We defined LinkedListIterator that referenced the
current node of a LinkedList.
38Improving LinkedListIterator (cont.)
- This works fine for get and set, but when we try
to implement remove and add, we notice that we
need a reference to the Node preceding the
current Node. - There are several solutions keep a pair of
references in the Iterator one to the current
Node and one to the preceding Node. - We could also simply keep a reference to the Node
preceding the current one. - Boundary cases are simplified if LinkedList has a
header.
39Iterator extensions
- We define Iterator extensions that capture the
notion of a circular list, and that permit
forward and backward traversal of a list. - We can ensure that an Iterator created by a
DoublyLinkedList implements the interface
CircularIterator. - Now the iterator can be moved backward of forward
in a circular fashion.
40Iterator extensions (cont.)
- public interface WrappingIterator extends
Iterator - An Iterator that returns to the beginning of the
enumeration when advanced past the last element
of the container. done is true if the Iterator s
referencing the first item of the enumeration,
having been advanced from the last item, or if
the container is empty. - public void advanced ()
- Advance this Iterator to the next item. Advance
to the first item if this Iterator is currently
referencing the last item in the enumeration. - require
- The container this Iterator is referencing
- is not empty.
41Iterator extensions (cont.)
- public boolean done ()
- This Iterator currently references the first
item of the enumeration having been advanced from
the last, or the container is empty. - public Object get ()
- Container item this Iterator currently
references. - require
- The container this Iterator is referencing
- is not empty.
42Iterator extensions (cont.)
- public interface BiDirectionalIterator extends
Iterator - An Iterator that can move to previous as well as
next item. offRight is true if the Iterator has
been advanced past the last element, or if the
container is empty. offLeft is true if the
Iterator has been backed up past the first
element, or if the container is empty. - public boolean done ()
- This Iterator has been advanced past the last
element, or the container is empty. Equivalent
to offRight.
43Iterator extensions (cont.)
- public boolean offRight ()
- This Iterator has been advanced past the last
element, or the container is empty. Equivalent
to done. - public boolean offLeft ()
- This Iterator has been backed up past the first
element, or the container is empty. - public void backup ()
- Move this Iterator back to the previous element.
44Iterator extensions (cont.)
public interface CircularIterator extends
WrappingIterator, BiDirectionalIterator An
Iterator can move to next or previous item in
the container, and wraps at the ends of the
enumeration. That is, moves to the first element
when advanced past the last element, and moves to
the last element when backed up from the first
element. If the container is not empty, the
Iterator references the first element of the
enumeration when offRight (done) is true. If the
container is not empty, the Iterator references
the last element of the enumeration when offLeft
is true. public void advance () Advance this
Iterator to the next item. Advance to the first
item if this Iterator is currently referencing
the last item in the enumeration. require
The container this Iterator is referencing
is not empty.
45Iterator extensions (cont.)
- public void backup ()
- Move this Iterator back to the previous item.
Move to the last item if this Iterator is
currently referencing the first item in the
enumeration. - require
- The container this Iterator is referencing
- is not empty.
46Iterators and List modification
- More than one Iterator can be traversing the same
list. - Suppose an Iterator i is referencing a particular
element of a List list, for instance the element
with index 2
47Iterators and List modification (cont.)
- The specifications promise that if we delete the
element referenced by the Iterator, the Iterator
will reference the next element.
48Iterators and List modification (cont.)
- The specifications promise that if we next do
list.add(i, x)
49Iterators and List modification (cont.)
- How does an Iterator behave if the List is
modified by means of another Iterator, or by
means of an index? - If we do
- list.remove(j) or l.remove(2)
- what will the state of i be?
50Iterators and List modification (cont.)
- We could build Iterators that are Observers of an
Observable List. - The Iterators register with the List.
- The List informs all its Iterators whenever it is
modified structurally. - Instead, we assume that if a List is modified by
index, all Iterators that reference the List
become invalid. - Because, Iterators typically have very local
scope and short lifetimes, this does not present
serious practical problems.
51Iterators and List modification (cont.)
- We could add methods to the class List that take
several Iterators as arguments. - public void remove (Iterator i1, Iterator i2)
- Delete all the elements of this List from the
element specified by the first Iterator through
the element specified by the second, inclusive. - The postconditions describe the state of the
Iterators after the method is executed.
52Internal or passive Iterators
- Internal or passive Iterators shift the
responsibility from the client to the iterator. - A client provides an operation and the iterator
applies the operation to each element of the
container. - When the Iterator is created, it is provided with
a List, a condition, and an operation.
53Internal or passive Iterators (cont.)
- public interface Predicate
- public boolean evaluate (Object obj)
-
- public interface Operation
- public void execute (Object obj)
-
54Internal or passive Iterators (cont.)
- The internal iterator constructor takes a List,
Predicate, and Operation as arguments. - Its single command applies the Operation to all
List elements that satisfy Predicate. - public class ListTraverse
- Internal iterator that performs a spcified
operation on each List element that satisfies a
given condition. - public ListTraverser (List list, Predicate p,
Operation op) - Create a new iterator with the specified List,
Predicate, and Operation. - public void traverse ()
- Apply the Operation to each element of the List
that satisfies the Predicate.
55Internal or passive Iterators (cont.)
- An Iterator to print out all students on
StudentList CourseList who have a final average
greater than 90. - ListTraverser listA new ListTraverser (
- new Predicate ()
- public boolean evaluate (Object obj)
- return ((Student)obj).finalAve()gt90
-
- ,
- new Operation ()
- public void execute (Object obj)
- System.out.println(
- ((Student)obj).name())
-
- ,
- courseList)
56Internal or passive Iterators (cont.)
- The Iterator defines anonymous classes.
- To perform the iteration, we give the Iterator
the traverse command - listA.traverse()
57Comparing implementations
58The java.util Collection hierarchy
- The standard Java package java.util defines the
interface Collection. - The interface Collection models a rather
generalized container. - public boolean contains (Object o)
- This collection contains the specified element.
- public boolean isEmpty ()
- This collection contains no elements.
- public int size ()
- The number of elements in this collection.
- public java.util.Iterator iterator()
- An iterator over the elements in this
collection.
59The java.util Collection hierarchy (cont.)
- Operations to add and remove elements are
optional. - Example
- public boolean add (Object o) throws
UnsupportedOperationException, ClassCastException,
IllegalArgument Exception
60The java.util Collection hierarchy (cont.)
- If the operation add is not supported by the
implementing class, an UnsupportedOperationExcepti
on is thrown. - If the class of the argument prevents it from
being added to the Collection, a
ClassCastException is thrown. - If any aspect of of the object other than its
class prevents it from being added to the
Collection, an IllegalArgument is thrown.
61List
- List is an interface that extends Collection.
- It includes
- public Object get (int index)
- The element at the specified position in this
List.
62Hierarchy
- Corresponding to the interfaces Collection, List,
and Set are abstract classes AbstractCollection,
AbstractList, and AbstractSet. - Array-based list implementations such as
java.util.Vector extend AbstractList directly. - Linked implementations, such as
java.util.LinkedList, extend the abstract class
AbstractSequentialList, which extends
AbstractList.
63Hierarchy (cont.)
64Iterators
- The java.util interface Iterator specifies three
methods - public boolean hasNext()
- The iteration has more elements.next
- public Object next () throws NoSuchElementExceptio
n - The next element in the interation. Throws
NoSuchElementException if hasNext() is false. - public void remove () throws UnsupportedOperationE
xception, IllegalStateException - Removes from the underlying collection the last
element returned by the iterator (optional
operation). This method can be called only once
per call to next.
65Iterator (cont.)
- next is not a proper query since it changes the
state of the iterator. - public Object next ()
- Object temp this.get()
- this.advance()
- return temp
66Weve covered
- Organizing list classes into a coherent library
structure. - Separating the List abstraction hierarchy from
the implementation hierarchy by use of a bridge. - Method performance.
- External Iterators.
- Internal Iterator abstraction.
- Collection interfaces and abstraction found in
the standard Java Package java.util.
67Glossary