Java: Alternative to iterator.hasNext() if using for-each to loop over a collection

41,003

Solution 1

for-each is just syntactic sugar for iterator version and if you check compiled bytecode, then you'll notice that compilator actually change it into iterator version.

With a for-each form you can't check whether you'll have more elements or not. Just stay with explicit iterator use if you need that feature.

Solution 2

In addition to Luno's answer:

Iterator<MyClass> it = myCollection.iterator();
while(it.hasNext()) {
  MyClass myClass = it.next():
  // do something with myClass
}

translates to:

for (MyClass myClass:myCollection) {
  // do something with myClass
}

Solution 3

As others have said - this isn't possible.

Just remember that the foreach construct isn't the be-all and end-all. It was introduced to make the very common task of performing the same operations on each element of a collection simpler to denote.

In your case, you don't want to do exactly the same thing to each element - and thus a foreach loop is not the right tool for the job. Trying to "hack" it into doing this is silly, just use an explicit iterator in a classic for loop.

Solution 4

The foreach loop (or enhanced for loop) does not have facilities to keep track of which element is being iterated on at the moment. There is no way to find out which index of a Collection is being worked on, or whether there are more elements to be processed in an Iterable.

That said, one workaround that would work is to keep a reference to the object which is being iterated on at the moment in the foreach loop.

By keeping a reference of what it being worked on at the current iteration, one would be able to keep the reference once the foreach loop ends, and what is left in the variable will be the last element.

This workaround will only work if-and-only-if the last element is the only element which is needed.

For example:

String lastString = null;

for (String s : new String[] {"a", "b", "c"}) {
    // Do something.

    // Keep the reference to the current object being iterated on.
    lastString = s;
}

System.out.println(lastString);

Output:

c

Solution 5

Unfortunately, the for each idiom does not allow you to check if an element is first or last in the list. This is a known limitation of the for each loop. I suggest you just keep using the iterator.

If you can also check for the first element instead of the last one, for example if you're doing String concatenation, you could change to something like:

boolean first = true;
for (Element e : list) {
  if (!first) {
    //do optional operation
  }
  //do other stuff
  first = false;
}

but I would prefer using the iterator.

Share:
41,003
user2161301
Author by

user2161301

Curiosity. Amoeba-shaped. Currently working on Moqups.

Updated on August 29, 2020

Comments

  • user2161301
    user2161301 over 3 years

    I'm trying to replace an iterator-based loop over a Java list with a for-each statement, but the code uses at some point iterator.hasNext() to check if it reached the last element in the list.

    Is there something similar for the for-each alternative?

    for (Object current : objectList) {
       if (last-element) 
          do-something-special
    }
    
  • Andreas Dolk
    Andreas Dolk over 14 years
    .. or if you need the Iterator#remove() method (if it's implemented on the actual Iterator)
  • Jorn
    Jorn over 14 years
    That can be quite expensive for large lists.
  • Mirek Pluta
    Mirek Pluta over 14 years
    We can iterate (with for-each) through any class that implements Iterable interface, it doesn't hae to be a list and it ofc doesn't have to provide any indexOf method
  • Andrzej Doyle
    Andrzej Doyle over 14 years
    Is that better than just using an explicit Iterator in the for loop though? I'd argue it's more verbose, less obvious what's going on and has a higher chance of logic errors than if !iter.hasNext().
  • Carl Smotricz
    Carl Smotricz over 14 years
    No, I agree it's a horrible solution. I'd retract it but I'll leave it here as a warning to others.
  • Joachim Sauer
    Joachim Sauer over 14 years
    Another problem here is that you assume that you've got a Collection (i.e. you have a size() method) and that it isn't expensive to call it. You can have a general Iterable object that doesn't provide a size() method. In that way the Iterator is the only way to go.
  • matt b
    matt b over 14 years
    ... or if you need to know the current index of an array you are iterating over
  • Kevin Bourrillion
    Kevin Bourrillion over 14 years
    ... that last part only being meaningful when it's a List or array; otherwise you need to create and increment a separate i anyway, so you may as well keep using the foreach loop.
  • Dingredient
    Dingredient over 7 years
    This can produce false positives, if the last word of the list occurs more than once in the list, then your condition will be met for each occurrence - not only the last occurrence.