8

I don't work too often with Java/C# iterators directly but when I do I always wonder what was the reason to design iterators in "next" fashion.

In order to start you have to move iterator, in order to check if there is some data you have to check if there is next element.

More appealing concept for me is this-iterator -- it "starts" from the beginning, and you can check this state, let's say isValid. So the loop over entire collection would look like this:

while (iter.isValid())
{
  println(iter.current());
  iter.next();
}

As you here in order to check if there is next element, you go there, and then you check if the state is valid.

YMMV but anyway -- is there any advantage of next-iterator (as in Java/C#) over this-iterator?

Note: this is conceptual question, about the design of the language.

3
  • question is for java or C# bzoc isValid method not exist in java
    – Niks Tyagi
    Commented Feb 3, 2014 at 17:02
  • @NiksTyagi It is a conceptual question that applies equally to both, as the proposed design is different from either language's implementation.
    – Servy
    Commented Feb 3, 2014 at 17:02
  • Each virtual call is costly. Your example has three calls, .net only two. Personally I'd prefer reducing it to one call. Commented Feb 4, 2014 at 10:10

2 Answers 2

6

I think an advantage of C#/.NET's MoveNext() model over Java's hasNext() model is that the former implies that some work may be done. The hasNext() method implies a simple state check. But what if you are iterating a lazy stream, and you have to go out to a database to determine whether there are any results? In that case, the first call to hasNext() may be a long blocking operation. The naming of the hasNext() method can be very misleading as to what's actually going on.

For a real world example, this design issue bit me when implementing Microsoft's Reactive Extensions (Rx) APIs in Java. Specifically, for the iterator created by IObservable.asIterable() to function properly, the hasNext() method would have to block and wait for the next item/error/completion notification to arrive. That's hardly intuitive: the name implies a simple state check. Contrast that to the C# design, where MoveNext() would have to block, which is not an altogether unexpected result when you are dealing with a lazily evaluated stream.

My point is this: the "next-iterator" model is preferable to the "this-iterator" model because the "this-iterator" model is often a lie: you may be required to pre-fetch the next element in order to check the state of the iterator. A design which communicates that possibility clearly is preferable, in my opinion. Yes, Java's naming conventions do follow the "next" model, but the behavioral implications are similar to those of the "this-iterator" model.


Clarification: Perhaps contrasting Java and C# was a poor choice, because Java's model is deceptive. I consider the most important distinction in the models presented by the OP to be that the "this-iterator" model decouples the "is valid" logic from the "retrieve current/next element" logic, while an ideal "next-iterator" implementation combines these operations. I feel it is appropriate to combine them because, in some cases, determining whether the iterator's state is valid requires prefetching the next element, so that possibility should be made as explicit as possible.

I do not see a significant design difference between:

while (i.isValid()) { // do we have an element?  (implied as fast, non-blocking)
    doSomething(i.current()); // retrieve the element (may be slow!)
    i.next();
}
// ...and:
while (i.hasNext()) { // do we have an element?  (implied as fast, non-blocking)
    doSomething(i.next()); // retrieve the element (may be slow!)
}

But I do see a meaningful difference here:

while (i.hasNext()) { // do we have an element?  (implied as fast, non-blocking)
    doSomething(i.next()); // retrieve the element (may be slow!)
}
// ...and:
while (i.moveNext()) { // fetch the next element if it exists (may be slow!)
    doSomething(i.current()); // get the element  (implied as fast, non-blocking)
}

In both the isValid() and hasNext() examples, the operation that is implied to be a fast and non-blocking state check may in fact be a slow, blocking operation. In the moveNext() example, most of the work is being done in the method you'd expect, regardless of whether you are dealing with an eagerly or lazily evaluated stream.

6
  • 1
    +0: I think OP tried to compare Java/C# vs. some other iterator (both C# IEnumrable and Java Iterator point to "before" elements when created). My understanding of the question is why not to point to first element instead rather than comparing C# vs.Java. Commented Feb 3, 2014 at 17:14
  • Thank you for the answer, but C# and Java uses the same model (both are focused on next element). Commented Feb 3, 2014 at 17:15
  • The C# and Java models are definitely not the same. See my added example. C#'s model combines the operations of checking for the presence of an element with fetching that element. Java splits these into separate operations, with the presence check sometimes necessitating actually fetching the item under the hood. It's actually quite similar to the OP's example: if you ignore the names of the methods, hasNext() is similar to isValid(), and next() is similar to current(). Commented Feb 3, 2014 at 17:19
  • I meant model in sense of THIS question. Ok, they are not identical but both are based on next element, when you iterate in both you have to go to next element at start, because both start before actual collection. Commented Feb 3, 2014 at 17:25
  • I maintain that the Java model is very similar to the "this-iterator" model in the question in how it actually works. I assert that the behavioral implications between "this-iterator" and "next-iterator" are more important than the naming conventions, and the Java model has similar behavioral implications to your "this-iterator" example. While my answer may have strayed into territory the OP had not considered in asking the question, I do not think that makes my answer any less valid. Commented Feb 3, 2014 at 17:30
7

If there is some computation that needs to be done to compute each value having to get the next value before ever asking for the first value allows you to defer the processing of that first value beyond the construction of the iterator.

For example, the iterator could represent a query that has not yet been executed yet. When the first item is requested, the database is queried for the results. Using your proposed design, that computation would either need to be done upon construction of the iterator (which makes it harder to defer execution) or when calling IsValid, in which case it's really a misnomer, as calling IsValid would actually be getting the next value.

8
  • Nice answer. You hit the same point as me, but beat me by a couple seconds. Have an upvote :). Commented Feb 3, 2014 at 17:03
  • 1
    Thank you for the answer, but I believe it is incorrect. To check if iterator is valid, you don't have to get the real element, just the position of the iterator. IOW it has the same impact as hasNext, only its state about current state, not the future one. Or taking another view -- compare this with C++ iterators, you check if the state is valid (current!=end) without touching actual data. Commented Feb 3, 2014 at 17:13
  • PS. Let's stick you your scenario -- I would I like to print the results, so I have to call hasNext at the start, here the query is executed, data are retrieved. In the second model, I call isValid, here the query is executed. So the point of the query execution is the same. Commented Feb 3, 2014 at 17:19
  • @greenoldman So when calling IsValid what you're actually doing is going and getting the next value. This means it's doing exactly the same thing as the existing iterator (it doesn't have the current valid from the start, without needing to call a method to go get it) it just has a mis-leading name.
    – Servy
    Commented Feb 3, 2014 at 17:22
  • 1
    Exactly. The "next-iterator" model is preferable to the "this-iterator" model because the "this-iterator" model is often a lie: you may be required to pre-fetch the next element in order to check the state of the iterator. Commented Feb 3, 2014 at 17:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.