Software Development Iterators Computer Science 209 Software Development Iterators
Implementing equals for Sets public boolean equals(Object other){ if (this == other) return true; if (! (other instanceof Set)) return false; List<E> otherSet = (Set)other; if (this.size() != otherSet.size()) return false; return this.containsAll(otherSet); } Works correctly, because order of items is not implied Worst-case quadratic running time
Implementing equals for Lists public boolean equals(Object other){ if (this == other) return true; if (! (other instanceof List)) return false; List<E> otherList = (List)other; if (this.size() != otherList.size()) return false; for (int i = 0; i < this.size(); i++) if (! this.get(i).equals(otherList.get(i))) return false; return true; } Works correctly by accounting for the order of the items get runs in constant time for ArrayList but in linear time for LinkedList Worst-case quadratic running time for LinkedList.equals
Implementing equals with iterator public boolean equals(Object other){ if (this == other) return true; if (! (other instanceof List)) return false; List<E> otherList = (List)other; if (this.size() != otherList.size()) return false; Iterator<E> otherIter = otherList.iterator(); for (E thisElement : this) if (! thisElement.equals(otherIter.next())) return false; return true; } Works correctly by accounting for the order of the items next runs in constant time for any collection’s iterator Worst-case linear running time for equals with lists
The Iterator Interface public interface Iterator<E>{ public boolean hasNext() public E next() public void remove() } remove deletes the item 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 collection D D D 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 collection D D D 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 collection D D D 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 collection D D D 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){ while (iter.hasNext()) if (iter.next().equals(o)){ 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.add(someItem); 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
A Naïve Iterator Implementation public class ArrayStack<E> extends AbstractCollection<E> implements TrueStack<E>{ private List<E> list; // Code for constructors, push, pop, peek, and size public Iterator<E> iterator(){ return list.iterator(); } Problem: a list’s iterator supports the remove method This would violate the spirit of a stack
Another Design Strategy By using the list’s iterator, we also expose the list to the client (violates the Law of Demeter) Let’s continue to use it, but wrap our own iterator object around it That allows us to control what’s supported and what’s not
An Iterator Implementation public class ArrayStack<E> extends AbstractCollection<E> implements TrueStack<E>{ private List<E> list; // Code for push, pop, peek, and size // 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>(this.iterator()); } private class StackIterator<E> implements Iterator<E>{ public boolean hasNext(){ return false; public E next(){ return null; public void remove(){ Nested within ArrayStack
The Implementing Class 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 The stack iterator encapsulates the list iterator, hiding it from the client (proxy pattern)
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 stacks"); Must add javadoc with preconditions and code to check them
For Wednesday GUIs with Swing and AWT