Iterators and Inner Classes

Created

Java Iterator and Inner Class


Enhanced For Loop

A way to iterate through all elements of a Collection. It is helpful iterating through a collection with no order (not all data structures are "positional"), such as a Set.

IndexedList<Integer> values = new ArrayIndexedList<>();
for (int val : values) {
	...
}

Changing val will not change the values in values

Enhanced for loop uses syntatic sugar:

Iterator<T> it = myCollection.iterator();
while(it.hasNext()) {
	MyType element = it.next();
}

for (MyType element: myCollection) {
	// same as above
}

Iterator Design

The iterator design pattern states the elements of a collection (an aggregate object) shoudl be accessed and traversed without expoing its representation (underlying implementation). This goal is achieved by defining an iterator object to traverse a data tructuer and access ts elemens.

Implement using Java API interfaces: Iterable and Iterator.

The built-in array is an exception because it is an iterable but does not implement the Iterable interface. This array is a unique construct, a cross between primitive an dobjects.

Iterable Interface Declare an ADT is iterable:

public interface IndexedList<T> extends Iterable<T> {
	...
	Iterator<T> iterator();
}

Implementing inherited iterator method in the ArrayIndexedList:

public class ArrayIndexedList<T> implements IndexedList<T> {
	@Override
	public Itereator<T> iterator() {
		return Arrays.stream(data).iterator();
	}
}

Must implement

import java.util.Arrays; 
import java.util.Iterator;

Instead of using Arrays.stream(data).iterator(), we can make our own iterator by creating a new class that implements the iterator interface.

public class ArrayIndexedListIterator<T> implements Iterator<T> {
	private T[] data;
	private int cursor;
	
	public ArrayIndexedListIterator(T[] data) {
		this.data = data;
		this.cursor = 0;
	}
	
	@Override public boolean hasNext() {
		return cursor < data.length;
	}
	
	@Override public T next() {
		if (!hasNext()) {  
			throw new NoSuchElementException();
		} 
		return data[cursor++]; 
	} 
}

Then in ArrayIndexedList.iterator:

public class ArrayIndexedList<T> implements IndexedList<T> {
	@Override public Iterator<T> iterator() { 
		return new ArrayIndexedListIterator<>(this.data); 
	} 
}

We can put this iterator class inside the data structure itself ("nested class"), The advantages to using an inner class:

  • Logical way of grouping classes that are only used in one place.
  • Increases encapsulation. The iterator can get access to all the members of the data structure. Furthermore, the iterator can be hidden from the outside world
  • More readable and maintainable.
  • Always use inner classes to impelment the Iterator interface for a data structure in this course.

Test an Iterable ADT

@Test
@DisplayName("test iterator after IndexedList is defined")
void testEnhancedLoopAfterConstruction() {
	int counter = 0;
 	for (int element: numbers) {
		assertEquals(defaultValue, element);
		count++;
	}
	assertEquals(size, counter);
}