views:

1438

answers:

8

I have an implementation of java.util.Iterator which requires that the call to next() should always be proceeded by a call to hasNext(). (This is because results are returned asynchronosly in a multi threaded environment and it is never clear how many more results there might be).

Would it be 'correct' to properly document this in the JavaDoc and then throw a RuntimeException if this was violated. Or is this stretching the Iterator interface a little too far?

All thoughts appreciated?

+9  A: 

I might be missing something here, but why not call hasNext() internally in your implementation?

Fabian Steeg
I could do that but I'd rather not - just want to enforce the pattern of usage which mean that the user should always call hasNext() first.
Dan
@Dan I always try to make it hard for the client to use an API the wrong way, and I think demanding something to be called before something else is called makes an API very easy to be used in the wrong way.
Fabian Steeg
Disagree - I think calling next() without first calling hasNext() is wrong. I would be enforcing the generally accepted correct usage.
Dan
I think you are not right here. Generally it is accepted, that in most cases routines should do the work they are assigned to without depending on a call-order. For instance, if you have some object over data and you want calculate a report, don't say the user of the API should call first calculateReport() and then multiple times getReportColumn(). Instead calculateReport should return a Report-object, that allows to accces the report-columns. Same here: next() should work without relying on a special call-order, especially since it is specified that you can call next() alone.
Mnementh
@Dan: Iterators are defined in the library with a particular set of semantics. Any usage that abides to those semantics is correct. When defining your own iterator if your semantics differ from the standard iterators you are probably confusing users and making your code `harder`. While I can only agree in that the correct usage is checking `hasNext()` before `next()`, the correct response from the iterator is throwing `NoSuchElementException` only if there are no more elements. When forcing correctness you should not only require it from your users but also from your own implementation.
David Rodríguez - dribeas
The Iterator interface does not mandate a particular call order, so if your implementation requires on you are violating the contract of the interface. Documenting in your implementation is not useful when instances may be passed to something that has no knowledge of your implementation (and may even predate it).
Laurence Gonsalves
+3  A: 

If your hasNext() and next() calls aren't in a synchronized block/method, it is not guaranteed that you will have elements even if you call hasNext() before next().

The contract of the Iterator interface is that NoSuchElementException should be thrown if there are no more elements. So proceed with the next() method until such an exception arises.

That said, take a look at the java.util.concurrent package - it has concurrent collections whose iterators may help you - i.e. you can use these collections and iterators instead of implementing your own.

Bozho
Maybe I was unclear - the Iterator will be accessed in a single threaded environment - internally results will be produced to it in a multi threaded environment.
Dan
the same iterator cannot be used in two different environments. If it is accessed in any way from multiple-threads, then it's he einvoronment is multi-threaded
Bozho
+2  A: 

I would rather throw an exception from next(), when there are no more elements. In a multi-threaded environment hasNext() is pretty useless anyway.

Joonas Pulakka
It's quite rare for an iterator to be designed to be shared between threads.
finnw
You don't need to share the *iterator*. Sharing the underlying *collection* is enough to make the iterator "essentially shared".
Joonas Pulakka
But yes, taking it into account is rare, as you wrote. But there definitely are practical uses for a thread-safe iterator; for instance, purging a queue using parallel consumers. It's quite useful when there are multiple processors/cores in use.
Joonas Pulakka
+3  A: 

I imagine you're doing something like this:

class IteratorImpl<T> implements Iterator<T> {
  private Source<T> source = ...
  private T next = null;

  public boolean hasNext() {
    if(next == null) {
      next = source.poll();
    }
    return next != null;
  }

That sounds OK to me. I can't imagine a situation where you'd want to use next without hasNext - it would be a recipe for exceptions.


EDIT:

The doc for hasNext() says:

Returns true if the iteration has more elements. (In other words, returns true if next would return an element rather than throwing an exception.)

To me, the implementation does not violate the contract. However, I would (as Fabian Steeg implies) still implement next() as:

  public T next() {
    if(!hasNext()) {
      throw new NoSuchElementException();
    }
    T ret = next;
    next = null;
    return ret;
  }

I mean, what does that check really cost you?

You must check and throw a NoSuchElementException as per the API contract. Either testing on !hasNext() or next == null will meet this criteria, I believe, but I would favour the former.

If someone is catching NoSuchElementException instead of calling hasNext(), you probably have bigger problems.

McDowell
Exactly what I want to do - but is Ucleman right that it voilates the Iterator contract?
Dan
@Dan - by my interpretation of the documentation, I don't believe this violates the contract.
McDowell
Pretty much every iterator I've ever written (which is many) has begun its next() implementation with those same three lines you illustrate here.
Kevin Bourrillion
This is the right answer, next() should always call hasNext() first in all but the most rare instances. Otherwise you are either a) duplicating code, or b) doing a different 'hasNext' check internally than what you are providing to the caller... which is dangerous from a consistency perspective.
PSpeed
+8  A: 

Requiring that hasNext() be called before next() violates the iterator contract. You really should rewrite it so that next() simply throws a NoSuchElementException if there is no element to return.

uckelman
Why does it violate the Iterator contract?
Dan
@Dan: Because `next()` has only two valid results: returning the next value or throwing a `NoSuchElementException` when there is no next element. Since you'd throw an exception, even if there were a next element, you're violating the contract.
Joachim Sauer
Agreed, throwing the NoSuchElementException is the correct behavior here. The hasNext() method is a guard method to avoid having to deal with the exception (as in if hasNext() returns true, next() should never throw the exception).
tylermac
+1  A: 

You could do something similar to the below, whereby you delegate the underlying data fetch to a private method, and implement hasNext() and next() to react differently to the absence of data. This has the advantage that you can repeatedly call next() without calling hasNext() first, and hence do not violate the contract of Iterator.

public class IteratorImpl<T> implements Iterator<T> {
  private final Source<T> source;
  private T next;

  public synchronized boolean hasNext() {
    tryGetNext();
    return next != null;
  }

  public synchronized T next() {
    tryGetNext();

    if (next == null) {
      throw new NoSuchElementException();
    } 

    return next;
  }

  private void tryGetNext() {
    if (next != null) {
      next = source.poll();
    }
  }
}
Adamski
That's actually what I did implement in the end!However I was just exploring the possibility of enforcing the order of method calls.
Dan
The use of volatile might not synch with the question, since it was stated as only be accessed by a single thread, is there another reason it's used here?Also, in tryGetNext(), since the operation is dependent on the value of next, shouldn't a AtomicReferenceFieldUpdater be used?
tylermac
Agreed - I'm not sure why I wrote it like this as it's clearly not thread-safe even with volatile; there's potential for 2 threads to both call next and both end up calling source.poll(). I'll amend to make it fully thread-safe.
Adamski
A: 

I would also suggest implementing the iterator as fail-fast

Helper Method
A: 

What you can do is implement the Iterator interface yourself (if you need to interface with other API) and ask your clients to implement your own stricter interface.

I created such a class in my functional programming library in order to easily implement an Iterator around a ResultSet in a project at work.

My class EasierIterator implements the Iterator interface while requiring that the client implements a simpler interface based on moveNext()/getCurrent(). It actually does the caching of the current item for you.

Vincent Robert