Tutorials
Iterators
- Data structures typically contain a collection of elements.
- It is often useful to be able to traverse that collection of elements.
- It is possible to get an iterator from any collection that implements the Iterable<T> interface.
- The
Iterable<T>interface is quite simple:Iterator<T> iterator();
- You may be wondering why you're seeing
<T>instead of<E>.- Skip these sub-bullets if you aren't the slight bit interested.
- The
EinList<E>refers to the type of element in the list. - The
TinIterable<T>refers to the type of element over which we can iterate. - Okay... at least I gave you the option of skipping these sub-bullets.
- So you're probably wondering what you can do with an iterator. If not, you should be.
Iterator<E>is an interface consisting of three methods:boolean hasNext()— returnstrueif another element exists in the collectionE next()— returns the next element in the collectionvoid remove()— removes, from the underlying collection, the last element returned by the iterator (the result of the most recentnext()call)
- That's all great, but who's going to implement the
Iterator<E>interface?- We need a class to implement the interface, but the class needs to be able to access the elements within the collection.
- Nested/Inner class to the rescue.1)
Revisiting the Simplified ArrayList<E> Example
- Here is how we could add the
Iterable<T>interface to our simplified ArrayList<E> class@Override public Iterator<E> iterator() { return new ArrayListIterator(); } private class ArrayListIterator implements Iterator<E> { private int position; private boolean isLegalToRemove; private ArrayListIterator(){ position = -1; isLegalToRemove = false; } @Override public boolean hasNext() { return size() > (position+1); } @Override public E next() { if(!hasNext()){ throw new NoSuchElementException("No next element available"); } isLegalToRemove = true; return array[++position]; } @Override public void remove() { if(!isLegalToRemove){ throw new IllegalStateException("Must call next() before remove()"); } isLegalToRemove = false; ArrayList.this.remove(position--); } } // End of ArrayListIterator class - You might think that we'd need to change the class declaration to this:
public class ArrayList<E> implements List<E>, Iterable<E> {- We don't need to do this because the
List<E>interface extends theCollection<E>interface which extends theIterable<E>interface.
- We don't need to do this because the
- Let's look at this code for a little bit...
- The
iterator()method was part of theArrayList<E>class before, we just hadn't implemented it here. - Our implementation just returns a reference to an object from the
ArrayListIteratorclass — the superhero inner class. - The
ArrayListIteratorclass and its constructor are declaredprivatesince nobody but theArrayList<E>class has any business messing with the class directly. - The
positionattribute keeps track of where we are in the collection. - The
isLegalToRemoveattribute is used by theremovemethod, and we'll talk about it in a bit. - The constructor forces us to begin one before the first element of the collection.
hasNext()needs to make sure that thepositionis such that we still have at least one more element in the collection to traverse.- We just make sure that we'll still have an element to return after incrementing the
position.
- We just make sure that we'll still have an element to return after incrementing the
next()moves the iterator to the next element and returns a reference to it.- First, we check to make sure that we have an element in the collection (if not we throw a
NoSuchElementExceptionexception). - Second, we increment the
positionattribute. Note: because we are using the pre-increment operator,positiongets incremented before we evaluate the rest of the expression. If we didposition++instead, we would get anArrayIndexOutOfBoundsExceptionexception the first time we callednext()because it would be trying this:array[-1]. - Third, we return a reference to the next element in the collection.
- First, we check to make sure that we have an element in the collection (if not we throw a
remove()is an optional method (meaning you can throw anUnsupportedOperationExceptionexception instead of implementing it), but theList<E>interface provides aremove(int index)method that does just what we want, so we'll call it.2)- The
remove()method should only be called ifnext()has been called since it the iterator was instantiated and since the lastremove()call. - As a result, we have introduced the
isLegalToRemoveflag to keep track of when it is legal to callremove().isLegalToRemoveis set tofalsein the constructor and wheneverremove()is called.isLegalToRemoveis set totruewhenevernext()is called.
- The
- The
Enhanced for Loop: The foreach Loop
- Iterators are what make it possible for us to use the enhance
forloop. - Consider:
List<Integer> list = new ArrayList<Integer>(); for(int i=0; i<5; ++i) { list.add((int)(Math.random()*25)); } for(Integer number : list) { System.out.println(number); }- The second
forloop makes use of an interator to navigate the collection. - It is equivalent to doing this:
Iterator<Integer> itr = list.iterator(); while(itr.hasNext()) { Integer number = itr.next(); System.out.println(number); }
- The second
Last modified: Monday, 29-Jul-2024 06:54:31 EDT