patternjavaMinor
Iterator for a list of objects
Viewed 0 times
listiteratorforobjects
Problem
Given a list of objects, return an iterator for such a list. Looking for code-review, optimizations and best practices. Verifying complexity to be O(n) where n is sum of all objects, including elements of collections if contained. previously solved here.
```
public class FlattenIterator implements Iterator {
private final Stack> iteratorStack;
private Object next;
public FlattenIterator(Iterable list) {
if (list == null) {
throw new NullPointerException();
}
iteratorStack = new Stack>();
iteratorStack.push(list.iterator());
}
private void moveToNext() {
while ((next == null) && !iteratorStack.empty()) {
Iterator itr = iteratorStack.peek();
// if iterator is empty, then pop it from stack.
if (!itr.hasNext()) {
iteratorStack.pop();
} else {
final Object next = itr.next();
if (next instanceof Iterable) {
iteratorStack.push(((Iterable) next).iterator());
moveToNext();
} else {
this.next = next;
}
}
}
}
/**
* Returns if there are any objects left to iterate over. This method
* can change the internal state of the object when it is called, but repeated
* calls to it will not have any additional side effects.
*/
public boolean hasNext() {
moveToNext();
return next != null;
}
/**
* Returns the next element in our iteration, throwing a NoSuchElementException
* if none is found.
*/
public Object next() {
moveToNext();
if (next == null) {
throw new NoSuchElementException();
}
Object objectToReturn = next;
next = null;
return objectToReturn;
}
public void remove() {
throw new UnsupportedOperationException();
}
}
public class FlattenIterat
```
public class FlattenIterator implements Iterator {
private final Stack> iteratorStack;
private Object next;
public FlattenIterator(Iterable list) {
if (list == null) {
throw new NullPointerException();
}
iteratorStack = new Stack>();
iteratorStack.push(list.iterator());
}
private void moveToNext() {
while ((next == null) && !iteratorStack.empty()) {
Iterator itr = iteratorStack.peek();
// if iterator is empty, then pop it from stack.
if (!itr.hasNext()) {
iteratorStack.pop();
} else {
final Object next = itr.next();
if (next instanceof Iterable) {
iteratorStack.push(((Iterable) next).iterator());
moveToNext();
} else {
this.next = next;
}
}
}
}
/**
* Returns if there are any objects left to iterate over. This method
* can change the internal state of the object when it is called, but repeated
* calls to it will not have any additional side effects.
*/
public boolean hasNext() {
moveToNext();
return next != null;
}
/**
* Returns the next element in our iteration, throwing a NoSuchElementException
* if none is found.
*/
public Object next() {
moveToNext();
if (next == null) {
throw new NoSuchElementException();
}
Object objectToReturn = next;
next = null;
return objectToReturn;
}
public void remove() {
throw new UnsupportedOperationException();
}
}
public class FlattenIterat
Solution
I'm going to hit the high notes on my phone and swing back later with details unless someone beats me to it.
Allow
Add a separate boolean field to track when
Avoid Recursion
In this case, there's no reason for
Use Generics
Even though you're stuck using
Testing
When using the
Burn this into your brain: Arrange, Act, Assert. Arrange the test fixture in the initial state, perform the action being tested, and assert the results. It can become tedious to setup the fixture each time, but helper methods can ease the pain and promote DRYness.
Allow
nullAdd a separate boolean field to track when
next holds the actual next element rather than using null as a signal that it needs to be populated so that null elements can be supported.Avoid Recursion
In this case, there's no reason for
moveToNext to call itself. Continuing the loop should have the same effect. Use Generics
Even though you're stuck using
Object for variables which may hold an element or an iterable, you can still add a generic type T to the class and specify it as the return type from next. At least, I think you can. :) Testing
When using the
@Test annotation, you don't have to prefix each test method name with test. However, you really need more descriptive method names, and each test should validate one specific condition. Burn this into your brain: Arrange, Act, Assert. Arrange the test fixture in the initial state, perform the action being tested, and assert the results. It can become tedious to setup the fixture each time, but helper methods can ease the pain and promote DRYness.
Context
StackExchange Code Review Q#56521, answer score: 5
Revisions (0)
No revisions yet.