1

I am aware of the conventional iterator creation-usage for a List<String> list as below:

//Conventional-style
Iterator<String> iterator = list.iterator()
while(iterator.hasNext()){
   String string = iterator.next();
   //...further code goes here
}

However, in the accepted answer of Iterating through a Collection, avoiding ConcurrentModificationException when removing in loop, I came across this unusual for loop usage with Iterator:

//Unconventional for loop style
for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {
    String string = iterator.next();
    //...further code goes here
}

Now, I'd like to know:

  1. Does this unconventional style create the iterator on the collection for each iteration over and over again? Or is it somehow a special kind of intelligent for-loop, which creates the iterator once and reuses it? If it creates an iterator each time, shouldn't it be a performance concern?
  2. Can we replace the while loop line in the conventional style with for(;iterator.hasNext();), if I were to use a for loop only?

PS: I am well aware of the enhanced for loop use on a collection. I am looking at this with the intention of 'safe' removal of elements, without causing a ConcurrentModificationException.

Community
  • 1
  • 1
Sarath Chandra
  • 1,850
  • 19
  • 40
  • 3
    That is quite a convential use of an iterator; and it creates the iterator only once (the first statement of a for loop, if any, is only ever executed once). In fact it is exactly equivalent to your first piece of code – fge Jul 25 '15 at 12:13
  • Not exactly equivalent, this way the `iterator` variable can't be used outside the `for` loop anymore, which is quite handy. – Clashsoft Jul 25 '15 at 12:17
  • 3
    Your first question is very strange (at least to me). Would ask the same if you see this `for (int i = 0; i < 10; i++)`? Would you also ask if `i` is recreated on each iteration? (And yes, this is the exact same type of a `for` loop) – Tom Jul 25 '15 at 12:24
  • @Tom: Yea.. It seems pretty silly now that I realized it. Thanks for pointing it out with such a simple straightforward example. I clearly missed it. – Sarath Chandra Jul 25 '15 at 12:26

3 Answers3

4

The idiom you call "unconventional" is actually the recommended one because it restricts the scope of the iterator variable to the loop where it is used.

  1. The iterator is created once, before the loop begins. This follows from the general semantics of the for loop, which I warmly advise you get acquainted with.

  2. You can, but you would not be recommended to. Such an idiom would be a pointless obfuscation of the while idiom.

Finally, note that for 99% of use cases all of the above is moot because you really should be using either the enhanced for loop or Java 8 forEach.

Marko Topolnik
  • 195,646
  • 29
  • 319
  • 436
  • Thanks a ton Marko.. The 'restricting the scope' part is new learning to me. I clearly missed the 'general semantics' of the for loop. Thanks for putting such a warm answer to my silly question. – Sarath Chandra Jul 25 '15 at 12:30
  • But the ´for(Iterator i = c.iterator(); i.hasNext(); ) { doSomething(i.next()); }´ was the best way to iterate over a collection prior to introducing Iterable. And still is if you want to use the i.remove() operation. – Jan Jul 25 '15 at 12:35
  • @jan `Iterable` on its own has nothing to do with it (but I guess you mean its connection with the _enhanced for_ loop). Yes, before Java 5 this was our best idiom and yes, there is that 1% of cases where you need to call anything else but `hasNext()` and `next()`. – Marko Topolnik Jul 25 '15 at 12:41
  • Ok because as the answer to the 2nd question you told OP that it is a pointless obfuscation of the while idiom. – Jan Jul 25 '15 at 12:45
  • @jan That's right, it most definitely is. Maybe you have misunderstood which idiom my statement pertains to, though. – Marko Topolnik Jul 25 '15 at 12:49
  • Your statement pertains to the forEach idiom. I can see that, but it has the downside, that you cannot modify the collection while iterating over it. So as the OP is "looking at this with the intention of 'safe' removal of element" you should have probably pointed out that if she wants to remove an element safely, he/she should use the for(... hasNext()...) way. As this is the preferred one if you want to do that. – Jan Jul 25 '15 at 12:53
  • No, my statement was that `for (;it.hasNext();) {...}` was a pointless obfuscation. – Marko Topolnik Jul 25 '15 at 13:09
2

Java is derived from C, and thus for (A; B; C) { P; } has the same semantics as A; while (B) { P; C; }. The only difference is the scope of the variables. In particular, the A part is only executed once. So your two code examples do exactly the same, but in the for-variant the scope of the variable is restricted.

The more modern way of iterating through a collection is the enhance for loop:

for (String string : list) {
    ...
}

However, if you want to delete or change items while iterating through it, you still need the iterator version. For example:

for (Iterator<String> it = list.iterator(); it.hasNext();) {
    String string = it.next();
    if (someFunction(string)) {
        it.delete();
    }
}

has no enhanced for-loop equivalent.

Hoopje
  • 12,677
  • 8
  • 34
  • 50
1

1. No, it does not create an iterator over and over again.. This was the perfectly fine style before Java included the interface Iterable<T>. If you want to remove an item while iterating over the collection you have to use the iterator.remove() method if it is provided.. Because otherwise a ConcurrentModificationException will be thrown.

If you do not want to remove an Item while iterating over the collection then you should just use the for each concept, which is provided by every collection that implements the Iterable<T> interface. (link in the end for more information)

for (String s : yourList) {
  ... // do something with the string
}

2. Yes!! Use the for loop idiom. But as I said, if you do not want to use the iterator.remove() operation, but just want to iterate over the collection, you should use the provided for each concept.

You can find a lot of information on the downsides of the iterator.next() approach here and why the newly integrated for:each concept is better: https://docs.oracle.com/javase/1.5.0/docs/guide/language/foreach.html

Jan
  • 2,025
  • 17
  • 27