Я читал, Почему Итератор Java не является Повторяемым? и Почему Перечисления не Повторяемы?, но я все еще не понимаю почему это:
void foo(Iterator it) {
for (X x : it) {
bar(x);
baz(x);
}
}
не был сделан возможным. Другими словами, если я не пропускаю что-то, вышеупомянутое, возможно, было хорошим и допустимым синтаксическим сахаром для:
void foo(Iterator it) {
for (X x; it.hasNext();) {
x = it.next();
bar(x);
baz(x);
}
}
, но я до сих пор не понимаю, почему это [...] не стало возможным.
Я вижу несколько причин:
Итераторы
не могут использоваться повторно, поэтому for / each будет использовать итератор - возможно, это не некорректное поведение, но неинтуитивно для тех, кто не знает, как for / каждый обессахарен. Итераторы
не так часто появляются в коде «голыми», поэтому они усложнили бы JLS с небольшим выигрышем (конструкция for / each и так достаточно плохая, работает с обоими Итерируемый
s и массивы). Iterables
, аналогичный Collections
и Arrays
, я не понимаю).) java.lang
[необходима цитата] , поэтому им придется создать интерфейс Iterator
в java.lang
, который java.util.Iterator
расширяется без добавления чего-либо. Теперь у нас есть два функционально эквивалентных интерфейса итератора. 50% нового кода, использующего голые итераторы, выберут версию java.lang
, остальные будут использовать версию java.util
. Наступает хаос, возникает множество проблем с совместимостью и т. Д.Я думаю, что пункты 1-3 очень соответствуют тому, как выглядит философия дизайна языка Java: не удивляйте новичков, не усложняйте спецификацию, если она не дает явного преимущества, которое затмевает затраты. , и не делайте с языковой функцией того, что можно сделать с библиотекой.
Те же аргументы объясняют, почему java.util.Enumeration
тоже не Iterable
.
Скорее всего, причина в том, что итераторы нельзя использовать повторно; вам нужно получать новый Iterator из коллекции Iterable каждый раз, когда вы хотите выполнить итерацию по элементам. Однако в качестве быстрого исправления:
private static <T> Iterable<T> iterable(final Iterator<T> it){
return new Iterable<T>(){ public Iterator<T> iterator(){ return it; } };
}
//....
{
// ...
// Now we can use:
for ( X x : iterable(it) ){
// do something with x
}
// ...
}
//....
Тем не менее, лучше всего просто передать интерфейс Iterable
вместо Iterator
Итераторы не предназначены для повторного использования (т. Е. Используются более чем в одном цикле итераций). В частности, Iterator.hasNext ()
гарантирует, что вы можете безопасно вызвать Iterator.next ()
и действительно получить следующее значение из базовой коллекции.
Когда один и тот же итератор используется в двух одновременно выполняющихся итерациях (допустим, в многопоточном сценарии), это обещание больше не может выполняться:
while(iter.hasNext() {
// Now a context switch happens, another thread is performing
// iter.hasNext(); x = iter.next();
String s = iter.next();
// A runtime exception is thrown because the iterator was
// exhausted by the other thread
}
Такие сценарии полностью нарушают протокол, предлагаемый Iterator. Фактически, они могут возникать даже в однопоточной программе: цикл итерации вызывает другой метод, который использует тот же итератор для выполнения своей собственной итерации. Когда этот метод возвращается, вызывающий вызывает вызов Iterator.next ()
, который, опять же, терпит неудачу.
Потому что for-each разработан для чтения примерно так:
for each element of [some collection of elements]
Итератор
не является [некоторый набор элементов]
. Массив и Iterable
есть.
Синтаксис for(Type t : iterable)
действителен только для классов, реализующих Iterable
.
Итератор не реализует iterable.
Вы можете выполнять итерацию над такими вещами, как Collection
, List
или Set
, потому что они реализуют Iterable.
Следующий код эквивалентен:
for (Type t: list) {
// do something with t
}
и
Iterator<Type> iter = list.iterator();
while (iter.hasNext()) {
t = iter.next();
// do something with t
}
Причина, по которой это невозможно, заключается в том, что синтаксис for-each был добавлен в язык, чтобы абстрагироваться от Iterator
. Заставив цикл for-each работать с итераторами, вы не добьетесь того, для чего был создан цикл for-each.