Разница между перемещением итератора вперед с помощью оператора for и оператора while

Когда я использую Iterator объекта, я использую цикл while (как написано в каждой книге, изучающей Java, как Мышление в Java Брюса Эккеля):

Iterator it=...

while(it.hasNext()){
    //...
}

но когда-то я видел, а вместо этого кто-то использовал цикл for:

Iterator it=...
for (Iterator it=...; it.hasNext();){
    //...
}

Я не понимаю этот выбор:

  • Я использую цикл for, когда у меня есть коллекция с порядковой последовательностью (как массив) или со специальным правилом для шага (обычно называемым простым приращением counter++).
  • Я использую цикл while, когда цикл заканчивается, когда у меня есть это ограничение, но только логическое условие для выхода.

Это вопрос о стиле-кодировании без какой-либо другой причины или существует какая-то другая логика (производительность, например), которую я не знаю?

Спасибо за каждую обратную связь

Ответы

Ответ 1

Правильный синтаксис цикла for:

for (Iterator it = ...; it.hasNext(); ){
    //...
}

(Предыдущее объявление в вашем коде лишнее, а также дополнительная точка с запятой в заголовке цикла.)

Используете ли вы этот синтаксис или цикл while - это вопрос вкуса, оба переводят точно так же. Общий синтаксис цикла for:

for (<init stmt>; <loop cond>; <iterate stmt>) { <body>; }

что эквивалентно:

<init stmt>;
while (<loop cond>) { <body>; <iterate stmt>; }

Изменить: Собственно, эти две формы не совсем эквивалентны, если (как и в вопросе) переменная объявлена ​​с помощью инструкции init. В этом случае будет разница в объеме переменной итератора. С циклом for область видимости ограничена самим циклом, однако в случае цикла while область действия распространяется до конца закрывающего блока (нет большого удивления, поскольку объявление находится за пределами цикла).

Кроме того, как отмечали другие, в новых версиях Java для цикла for имеется сокращенная запись:

for (Iterator<Foo> it = myIterable.iterator(); it.hasNext(); ) {
    Foo foo = it.next();
    //...
}

может быть записана как:

for (Foo foo : myIterable) {
    //...
}

С этой формой вы, конечно, теряете прямую ссылку на итератор, что необходимо, например, если вы хотите удалить элементы из коллекции во время итерации.

Ответ 2

Это просто стиль и, как и вы, я предпочитаю цикл for при использовании чего-то с индексом. Если вы используете Java 5, вы должны, конечно, использовать цикл foreach над коллекцией:

Collection<String> someCollection = someCollectionOfString();
for (String element : someCollection) {
....
}

Ответ 3

Цель объявления Iterator в цикле for заключается в том, чтобы свести к минимуму объем ваших переменных, что является хорошей практикой.

Когда вы объявляете Iterator вне цикла, тогда ссылка остается действительной/живой после завершения цикла. 99,99% времени, вам не нужно продолжать использовать Iterator после завершения цикла, поэтому такой стиль может привести к ошибкам, подобным этому:

//iterate over first collection
Iterator it1 = collection1.iterator();
while(it1.hasNext()) {
   //blah blah
}

//iterate over second collection
Iterator it2 = collection2.iterator();
while(it1.hasNext()) {
   //oops copy and paste error! it1 has no more elements at this point
}

Ответ 4

Существует не так много различий между этими двумя методами, кроме первого, который запоминает значение it после цикла. Второй подход, вероятно, вызывает ошибку, поскольку вы переопределяете переменную внутри цикла.

На самом деле это третий способ. Вместо записи, например,

for (Iterator<SomeObject> it = foo.iterator(); it.hasNext();) {
  SomeObject so = foo.next();
}

вы можете чаще всего писать

for (SomeObject so: foo) {...}

Эти два равны одному и тому же...

Ответ 5

Люди могут использовать явный ( "старый стиль" ) для цикла просто потому, что он чувствует себя более чистым для них, возможно, они еще не настроены на новый синтаксис (что я лично считаю намного более чистым).

Одно фактическое конкретное преимущество конструкции long for loop заключается в том, что у вас есть ссылка на итератор, и поэтому можно вызывать методы на нем иначе, чем next(). В частности, hasNext() часто может быть полезным для вызова - представьте, хотите ли вы распечатать список разделенных запятыми строк:

StringBuilder sb = new StringBuilder();
for (Iterator it=...; it.hasNext();){
   sb.append(it.next());
   if (it.hasNext())
      sb.append(", ");
}

Следует только различать случай "это последний", если вы используете более сложный цикл.