270 likes | 348 Views
Computer Science 209. Software Development Iterators. The Problem. A collection contains elements Clients need to access the elements The collection should not expose its internal structure There may be multiple clients that need simultaneous access. The Solution.
E N D
Computer Science 209 Software Development Iterators
The Problem • A collection contains elements • Clients need to access the elements • The collection should not expose its internal structure • There may be multiple clients that need simultaneous access
The Solution • Define an iterator class that fetches one element at a time • Each iterator object tracks the position of the next element • Because there might be multiple collection and iterator classes, it is best to have a single iterator interface
The Iterator Interface public interface Iterator<E>{ public boolean hasNext() public E next() public void remove() } remove deletes the object most recently accessed with next remove must be included in the implementing class, but need not be supported (can throw an UnsupportedOperationException)
Using an Iterator // add a bunch of objects to col Iterator<SomeType> iter = col.iterator(); Every collection class that supports an iterator must provide an iterator method. This method returns an instance of a class that implements the Iterator interface A sequence of elements anIterator aCollection
Using an Iterator // add a bunch of objects to col Iterator<SomeType> iter = col.iterator(); while (iter.hasNext()){ SomeType obj = iter.next(); System.out.println(obj); } collection D D D iterator
Using an Iterator // add a bunch of objects to col Iterator<SomeType> iter = col.iterator(); while (iter.hasNext()){ SomeType obj = iter.next(); System.out.println(obj); } collection D D D iterator
Using an Iterator // add a bunch of objects to col Iterator<SomeType> iter = col.iterator(); while (iter.hasNext()){ SomeType obj = iter.next(); System.out.println(obj); } collection D D D iterator
Using an Iterator // add a bunch of objects to col Iterator<SomeType> iter = col.iterator(); while (iter.hasNext()){ SomeType obj = iter.next(); System.out.println(obj); } collection D D D iterator
Iterable and the for-each Loop // add a bunch of objects to col for (SomeType obj : col) System.out.println(obj); If col implements the Iterable interface, the client can use a for-each loop instead collection D D D iterator
Use in AbstractCollection abstract public class AbstractCollection<E> implements Collection<E>{ public void clear(){ Iterator<E> iter = this.iterator(); while (iter.hasNext()){ iter.next(); iter.remove(); } } public boolean remove(Object o){ Iterator<E> iter = this.iterator(); while (iter.hasNext()) if (iter.next().equals(o)){ iter.remove(); return true; } return false; }
Preconditions on Methods public boolean hasNext() public E next() • hasNext has no preconditions • next has two preconditions: • hasNext returns true • the underlying collection has not been modified by one of • that collection’s mutators during the lifetime of that • iterator
Error: Run Out of Elements // Add a bunch of objects to col Iterator<SomeType> iter = col.iterator(); while (iter.hasNext()){ SomeType obj = iter.next(); <blah blah blah> } SomeType obj = iter.next(); // This should cause an exception
Error: Inconsistent Data // Add a bunch of objects to col Iterator<SomeType> iter = col.iterator(); while (iter.hasNext()){ col.removeLast(); SomeType obj = iter.next(); // This should cause an exception } Using mutators in conjunction with iterators is a bad practice
An Iterator Implementation public interface TrueStack<E> extends Collection<E>{ public E pop(); public void push(E newElement); public E peek(); } <<Interface>> Iterable <<Interface>> Collection <<Interface>> TrueStack The iterator method is in the Iterable interface
An Iterator Implementation public class ArrayStack<E> extends AbstractCollection<E> implements TrueStack<E>{ private List<E> list = new ArrayList<E>(); // Code for push, pop, peek, size, and add public Iterator<E> iterator(){ return list.iterator(); } } Problem: a list’s iterator supports the remove method
An Iterator Implementation public class ArrayStack<E> extends AbstractCollection<E> implements TrueStack<E>{ private List<E> list = new ArrayList<E>(); // Code for push, pop, peek, size, and add // Code for the iterator method // Code for the class that implements the Iterator // interface } Define the iterator class as a private inner class.
The Implementing Class public Iterator<E> iterator(){ return new StackIterator<E>(list); } private class StackIterator<E> implements Iterator<E>{ public boolean hasNext(){ return false; } public E next(){ return null; } public void remove(){ } } Nested within ArrayStack
Tracking Modifications private int modCount = 0; // An ArrayStack instance variable used to test // for concurrent modifications modCount is set to 0 when the collection is created. modCount is incremented whenever the collection is modified by one of its mutators. modCount is compared to the iterator’s expected mod count as a precondition for next
Data Within the Iterator private class StackIterator<E> implements Iterator<E>{ private int curPos, expectedModCount; private List<E> list; private StackIterator(List<E> list){ curPos = 0; expectedModCount = modCount; this.list = list; } // Other Iterator methods } Track the iterator’s current position and its notion of the mod count Note that the current position begins at the bottom of an ArrayStack (supports bottom to top traversal)
Implementation of hasNext private class StackIterator<E> implements Iterator<E>{ private int curPos, expectedModCount; private List<E> list; public boolean hasNext(){ return curPos < list.size(); } }
Implementation of next private class StackIterator<E> implements Iterator<E>{ private int curPos, expectedModCount; private List<E> list; public E next(){ if (modCount != expectedModCount) throw new ConcurrentModificationException( "Cannot mutate in context of iterator"); if (! hasNext()) throw new NoSuchElementException( "There are no more elements"); curPos++; return list.get(curPos – 1); } }
Implementation of remove private class StackIterator<E> implements Iterator<E>{ private int curPos, expectedModCount; private List<E> list; public void remove(){ throw new UnsupportedOperationException( "remove not supported by Stack"); } } Removing objects with an iterator would violate the spirit of the TrueStack interface, which stipulates that items can only be removed from the top of the collection
What about a Linked Stack? • Could try to use the same code for the iterator as in ArrayStack • Problem: get on linked list runs in linear time! private class StackIterator<E> implements Iterator<E>{ private int curPos, expectedModCount; private List<E> list; public E next(){ … return list.get(curPos – 1); } }
Another Design Strategy • We used the list’s iterator earlier, but by exposing it to the client (Law of Demeter?) • Let’s use it, but wrap our own iterator object around it • That allows us to control what’s supported and what’s not
The Implementing Class, v2 public Iterator<E> iterator(){ return new StackIterator<E>(list.iterator()); } private class StackIterator<E> implements Iterator<E>{ private Iterator<E> iter; private StackIterator(Iterator<E> iter){ this.iter = iter; } // Other methods } Nested within ArrayStack
Other Methods private class StackIterator<E> implements Iterator<E>{ private Iterator<E> iter; public boolean hasNext(){ return iter.hasNext(); } public E next(){ return iter.next(); } public void remove(){ throw new UnsupportedOperationException( "remove not supported by Stack"); } }