EECE 310: Software Engineering Iteration Abstraction Learning Objectives • Define the basic elements used in iterators • Use iterators to iterate over a collection • Implement iterators and the associated generator classes for an ADT • Understand the design issues for iterators • Design iterators that support concurrency Problem • Consider the IntSet ADT introduced earlier • Let’s say you want to compute the sum of elements in the set. How would you do it ? – Add a member function computeSum – Copy the rep to another array and return it – Add an indexing operation to the ADT – Add a new iterator abstraction • Which method would you choose and why ? Iteration Abstraction • General way to iterate over the elements of an ADT without tying the iteration to a specific operation (e.g., summation) – Efficient in space and time (as far as possible) For each element e of the ADT: Perform operation o on e Implementation • Implemented using two entities: 1. Generator: Keeps track of the state of the iteration and exposes operations to actually perform the iteration 2. Iterator: Operation of an ADT that returns a generator object to begin the iteration process Iteration Abstraction: Generators ADT state 1 Client Code 1 Generator objects state 2 Client Code 2 Client Code 3 Generators : Keeping track of State • Objects that keep track of the state of the iteration in its rep – Implements the Iterator interface in Java – Consists of methods hasNext and next – next method throws NoSuchElementException • Multiple generators can operate on an ADT • Generators can be passed around to procedures Generators: Example public interface Iterator<T> { public boolean hasNext() // EFFECTS: Returns true if there are more elements to // yield, else returns false public T next( ) throws NoSuchElementException // MODIFIES: this // EFFECTS: If there are more results to yield, // returns the next result and records the state of this // Otherwise, throws NoSuchElementException Iterator: Produces a generator • Operation of an ADT or a stand-alone procedure that returns a generator object – Different iterators can produce different generators (e.g., forward and reverse iterator) – Multiple calls to the iterator can produce different generators (for local iteration) • The specification of the iterator defines the behavior of the generator object it returns – add a requires clause at the end constraining its use Iterator: IntSet Example public class IntSet { ... public Iterator<Integer> elements ( ) // EFFECTS: returns a generator that will produce // all the elements of this, each exactly once, // in arbitrary order. // REQUIRES: this must not be modified while // the generator is in use. } Learning Objectives • Define the basic elements used in iterators • Use iterators to iterate over a collection • Implement iterators and the associated generator classes for an ADT • Design iterators that support concurrency How do we use iterators ? ADT (e.g., IntSet) Has iterator methods that return generator objects, e.g., elements Generator object: implements the Java iterator interface): 1. hasNext 2. next Step 1: Client invokes the iterator operation on ADT and stores the returned generator object IntSet s = new IntSet(v); …. Iterator g = s.elements(); Step 2: Perform the iteration using the hasNext and next methods of the generator object while ( g.hasNext() ) { Integer o = g.next( ); int i = o.intValue(); } Optional Step: May pass the generator object to different procedures for doing the operation e.g., int m = computeMax(g); Example: Using Iterators public static int max(Iterator<Integer> g) throws EmptyException { // REQUIRES: g!=null && g contains only Integers // MODIFIES: g // EFFECTS: Throws EmptyException if no elements in g, otherwise return // the maximum element of the collection try{ int m = ( g.next() ).intValue() ; while ( g.hasNext() ) { int x = ( g.next() ).intValue(); if (m < x) m = x; } return m; } catch( NoSuchElementException e) { throw new EmptyException(“Comp.max”); } } Some points on using Generators • Using code interacts with a generator via the iterator interface (defined in java.util package) • Using code must obey the constraint imposed on it by the iterator’s requires clause (given after the EFFECTS clause) • Generators can be passed as arguments and returned as results by procedures • Generators may be primed before using them in loops • Generators may loop infinitely, e.g., list of prime numbers In-class Exercise • Write a procedure that iterates over the elements of the IntSet ADT and prints all Integers that exceed a certain threshold, max How would you do this if you had an iterator that allowed you to choose elements that fall within a certain range, say, min and max. Learning Objectives • Define the basic elements used in iterators • Use iterators to iterate over a collection • Implement iterators and the associated generator classes for an ADT • Understand the design issues for iterators • Design iterators that support concurrency Implementing Iterators public class IntSet { private vector<Integer> els; // The rep ... public Iterator<Integer> elements ( ) // EFFECTS: returns a generator object that will // produce all the elements of this, each exactly // once, in arbitrary oder. // REQUIRES: this must not be modified while // the generator is in use. return new IntSetGen(this); } Implementing generators • All generators implement Java’s iterator interface – Using code interacts with it through the interface – You must implement the methods hasNext and next • Generator has access to the rep of the ADT – Must not be exposed to the using code • Generator has its own rep to store the state of the iteration which also should not be exposed IntSetGen: Scope • IntSetGen is declared as a nested class within the IntSet ADT and it’s scope is private – Clients cannot create IntSetGen objects except through the iterator method of the IntSet ADT – Clients cannot directly access any operation of IntSetGen except through the iterator interface – IntSetGen has undeterred access to the private methods of the IntSet including its rep IntSetGen: Declaration public class IntSet { private vector<Integer> els; // The rep public Iterator<Integer> elements( ) { // Iterator // EFFECTS: … return new IntSetGen( this ); } private static class IntSetGen implements Iterator<Integer> { // Generator object returned by elements } IntSetGen: Rep and Constructor private static class IntSetGen implements Iterator<Integer> { private int index; private IntSet s; Passed in by the elements() method of the IntSet ADT // Constructor IntSetGen(IntSet is) { // REQUIRES: is!= null // EFFECTS: Initializes the generator for a // new iteration cycle s = is; index = 0; } IntSetGen: hasNext operation public boolean hasNext( ){ // EFFECTS: If not reached the end // the elements vector, return true // otherwise, return false if ( index == s.els.size( ) ) return false; IntSet ADT’s Rep – can else access this because it is return true; declared as a } nested class IntSetGen: next operation public Integer next( ) throws NoSuchElementException { // EFFECTS: If there is a next object, return it // Otherwise, throw the NoSuchElementException if ( hasNext( ) ) return s.els.get(index++); else throw new NoSuchElementException(“IntSet.elements”); } Use the name of the iterator method that produced the generator object In-class exercise • Implement an Iterator for the IntSet ADT, to return all integers of the IntSet that satisfy a given critereon. The iterator takes as its argument a type Check with a public method: public boolean checker(Integer) - that returns true if and only if the Integer satisfies the criteria Assume that the Check class is given to you. Also, write the implementation of its associated generator object. Learning Objectives • Define the basic elements used in iterators • Use iterators to iterate over a collection • Implement iterators and the associated generator classes for an ADT • Understand the design issues for iterators • Design iterators that support concurrency Design Issues: Multiple Iterators • Types may sometimes have multiple iterators • Examples: – ForwardIterators, ReverseIterators for ordered collections such as lists – Iterators that return elements satisfying a certain critereon (say, within a specified range) – Iterators that perform some operation on their elements prior to returning them (e.g., sorting) Design Issues: Changes during Iteration • Default behavior so far: Disallow changes during iteration (specified in post-REQUIRES clause) • If changes are allowed, what are its semantics ? – State of iteration is what it was when the generator was created (requires creating copy) – Iteration sequence changes when changes occur to ADT (difficult to ensure consistency of state) • How do we reset iterator to go back in the collection ? • What if the current element is removed in a list ? Design Issues: Can the generator itself change the ADT ? • The generator can change the rep of the ADT as long as it does not change the abstraction – Example: An iterator that returns the elements of an un-ordered collection such as set in sorted order may sort the collection during the process of iteration • Iterator changes the abstraction exposed by ADT – This is usually best avoided unless there is a compelling need. For example, iterating over a task list may itself spawn new tasks to be added to the list. Testing Iterators • Iterators are tested like procedures, except that they have paths in their specifications that are similar to loops in procedures – Should test for returning generators that produce 0, 1, 2 results/elements • Generators are tested like any other ADT (Boundary conditions, aliasing etc.) Learning Objectives • Define the basic elements used in iterators • Use iterators to iterate over a collection • Implement iterators and the associated generator classes for an ADT • Understand the design issues for iterators • Design iterators that support concurrency Concurrent Iterators: Read only • Assume that we want to iterate over a list in two separate threads, but without modifying it. Is this allowed ? – Yes, as long as we keep the generator objects separate. i.e., we do not share generators – Each generator keeps track of where it is in the iteration – no changes to the state of the ADT – We need to make the iterator method synchronized (why ?) Concurrent Iterators: Read-Write Thread 1 Thread 2 Iterator<Integer> g = is.elements(); is.add(1); is.add(2); is.add(3); is.add(4); is.remove(2); while ( g.hasNext() ) { Integer i = g.next(); System.out.println(i + “ “); } No guarantees about what iterator returns How to solve the concurrent iterator problem ? • Disallow concurrent writes by requiring clients to take a lock before the iteration operation – Also disallows concurrent reads – performance ! – Need to explicitly release lock when done • Make a copy of the collection before iteration • Throw a concurrent modification exception Making a copy • Create a copy of the list when you call the iterator method, and perform iteration over it – Need to ensure that copy operation is performed in the iterator method in synchronized mode – Can be expensive to copy large lists in memory – Need to perform deep copy of elements to disallow any modifications – Iterator may operate on stale version of collection Concurrent modification exception • Exception thrown when the ADT is modified concurrently with the iteration – Thrown to the thread that performs the iteration – Up to the thread what it wants to do with it – Only one exception per concurrent modification will be thrown • ConcurrentModification is a checked exception CME: Usage public ... search (List list) { try { ListIterator iterator = list.listIterator(); while(iterator.hasNext()) { Object o = iterator.next(); //do something ... } } catch (ConcurrentModificationException concEx) { //handle this case, maybe just iterate the collection again return search(list); } } CME: Implementation - 1 02 * The number of times this list has been structurally modified. 03 * Structural modifications are those that change the size of the 04 * list, or otherwise perturb it in such a fashion that iterations in 05 * progress may yield incorrect results. ….. 15 * Use of this field by subclasses is optional. If a subclass 16 * wishes to provide fail-fast iterators (and list iterators), then it 17 * merely has to increment this field in its add(int, Object) and 18 * remove(int) methods (and any other methods that it overrides 19 * that result in structural modifications to the list). A single call to 20 * add(int, Object) or remove(int) must add no more than 21 * one to this field, or the iterators (and list iterators) will throw 22 * bogus ConcurrentModificationExceptions. If an implementation 23 * does not wish to provide fail-fast iterators, this field may be 24 * ignored. 25 */ 26 protected transient int modCount = 0; CME: Implementation - 2 30 private class Itr implements Iterator<E> { 31 int expectedModCount = modCount; 32 33 public boolean hasNext() { 34 // implementation omitted 35 } 36 37 public E next() { 38 checkForComodification(); 39 // implementation omitted 40 41 } 42 43 private void checkForComodification() { 44 if (modCount != expectedModCount) 45 throw new ConcurrentModificationException(); 46 } 47 } Class Exercise • Add a concurrent iterator to the IntSet ADT that throws a ConcurrentModificationException when another thread attempts to modify/remove from the set during the iteration. Learning Objectives • Define the basic elements used in iterators • Use iterators to iterate over a collection • Implement iterators and the associated generator classes for an ADT • Design iterators that support concurrency