Thursday, 24 November 2005

Adding for loop indexing to Java

When using the new for loop in JDK 1.5 do you ever need an index? What do you do, and could a language change help?

Collection coll = ...
int i = 0;
for (String str : coll) {
  System.out.println(str + ", line " + i);
  i++;
}

This isn't very friendly, and exposes the loop index outside the scope of the loop. Maybe we could have a small language change:

Collection coll = ...
for (String str : coll : int i) {
  System.out.println(str + ", line " + i);
}

Note that the extra : int i would be optional! If you don't want the index, just don't declare it.

This is really just a logical extension of the enhanced for loop change from JDK 1.5. Its main benefit is protecting against stupid errors with the scope of the index when an index is actually needed.

Something else that was lost with the new for loop was the ability to remove an item from the collection using the iterator. A variation on the index syntax would allow us to reinstate that:

Collection coll = ...
for (String str : coll : Iterator it) {
  if (str.startsWith("org.apache.")) {
    it.remove();
  }
}

There are more issues with this one however, as Iterator also exposes the hasNext and next methods which the for loop is using. This shouldn't cause a problem, but might require some more work in detailed design.

This blog is number 4 in a series on possible language enhancements to remove boilerplate code - see code blocks, nulls and casts. Opinions welcome as always :-)

6 comments:

  1. I'm not sure about the specific proposed syntax here, but I definitely agree that something should be done for these use cases.

    It's astonishing how often I end up converting "foreach" style stuff back to regular old "for" or "while" loops just to get these things. And the reasons are essentially always either to get an index in a more convenient way, or to get access to the iterator in order to be able to call .remove().

    Not having this in the new foreach syntax already was a big mistake, IMO.

    ReplyDelete
  2. This problem isn't specific for the enhanced for loop: it already existed in the "normal" for loop. When you used an Iterator, and needed an index, you also had to declare the int outside the loop scope. In those cases, I would prefer to be able to declare varying types in the for-initializer:

    for (Iterator iter = getIter(), int i=0; iter.hasNext();) ....

    Note also that, in your syntax, you don't specify the offset (does i start at 0 or at 1?) nor the increment (maybe I want an increment of 7?)

    (Your posts definitely cause some brain gymnastics, and give a refreshing look at the syntax. Keep it up!)

    ReplyDelete
  3. Really? The proposal for the foreach construct very clearly said that it was intended to provide syntactic sugar for the *common case* (read 80% of all usage), and in other cases, you could always fall back on the classic for loop. Is using the classic for loop such a brain gymnastic? Is it worth complicating a simple construct?

    ReplyDelete
  4. Yes, I think it's clearly worth it. The syntax could be a little nicer, but something along these lines would be really helpful.

    Maybe something like:
    foreach (String item; itemList; myIter) {
    int idx = myIter.getIndex();
    myIter.remove();
    }

    Could be quite nice to work with. :)

    ReplyDelete
  5. I'm with Sumit on this one.

    Yes, those cases come up, but I'm inclined to believe the simple case is sufficient for many purposes and the others are easily handled by the original syntax options. I'm not sure it's worth the additional effort. Could it be useful? Sure, it could. Worth the trouble and changes to language syntax? Not so sure.

    ReplyDelete
  6. Why not just use the remove(Object) method of collection?

    Collection coll = ...
    for (String str : coll ) {
    if (str.startsWith("org.apache.")) {
    coll.remove(str);
    }
    }

    ReplyDelete