#java #collections #iterator #decorator
Вопрос:
Лично я нахожу широкий спектр функций, предоставляемых java.util.Итератор должен быть довольно жалким. Как минимум, я хотел бы иметь такие методы, как:
- функция peek() возвращает следующий элемент, не перемещая итератор вперед
- функция previous() возвращает предыдущий элемент
Хотя есть много других возможностей, таких как first() и last().
Кто-нибудь знает, существует ли такой сторонний итератор? Вероятно, его нужно было бы реализовать как декоратор java.util.Итератор, чтобы он мог работать с существующими коллекциями java. В идеале это должно быть «знание дженериков».
Заранее спасибо, Дон
Ответ №1:
Вы можете previous()
легко добраться, просто используя a java.util.ListIterator
.
Заглянуть в этот момент легко реализовать, выполнив
public <T> T peek(ListIterator<T> iter) throws NoSuchElementException {
T obj = iter.next();
iter.previous();
return obj;
}
К сожалению, будет проще использовать его в качестве служебного метода, поскольку каждый класс коллекции реализует свои собственные итераторы. Сделать оболочку, чтобы получить метод заглядывания в каждую коллекцию на каком-то интерфейсе, например, MyListIterator
было бы довольно много работы.
Ответ №2:
Комментарии:
1. Спасибо, но AFAIK коллекции Apache commons все еще не были обобщены
2. обе эти связи теперь мертвы
3. исправил ссылки, спасибо. Apache теперь также поддерживает генераторы, и коллекции Google доступны как часть guava.
4. Поддерживает ли какой-либо итератор в Google Commons оба
peek()
иprevious()
?PeekingIterator
поддерживаетpeek()
, но нетprevious()
.
Ответ №3:
Я думаю, что причина, по которой они не реализованы, заключается в том, что они нетривиальны для некоторых коллекций и окажут большое влияние на производительность. Я думаю, что для вас было бы довольно просто сделать эту работу для коллекций, которые вам небезразличны.
Мне также не нравится, что итераторы Java не могут получить текущее значение без его перемещения (и поэтому вы не можете легко написать код, который разветвляется на основе значения, просто передавая итератор-вы также должны передать значение, которое у вас теперь есть).
Ответ №4:
Существует чертовски веская причина, по которой универсальные операторы не реализуют эти функции: они существуют не для всех контейнеров. Типичным примером является контейнер, представляющий некоторые внешние входные данные, например файл, рассматриваемый как поток. Каждый раз, когда вы читаете значение, вы используете его и перемещаете указатель вперед, хотите вы этого или нет. Если вы наложите эти ограничения на универсальные итераторы, вы потеряете универсальность итераторов.
Если вам нужен previous
метод, как было предложено, используйте ListIterator<>
, который затем ограничивается контейнером, ведущим себя как списки.
Комментарии:
1. Аргумент файла на самом деле неубедителен, нет причин, по которым мы не могли бы получить текущий символ (или что бы это ни было, что мы читаем), не перемещая курсор в файле. Однако я согласен с тем, что эта
previous
функция не может быть предоставлена каждым итератором (особенно итераторами «генератора», которые генерируют новое значение на каждом шаге).2. @Люк: это потому, что вы видите файлы как произвольный доступ. И они действительно не должны быть такими. Если вы предпочитаете, рассмотрите поток, такой как сетевые сокеты. Вы читаете то, что получаете, и если вы хотите читать дальше, вам нужен целый механизм кэширования. Поэтому, если вы хотите, чтобы ваш итератор использовался во всех ситуациях, вам действительно нужен интерфейс, предоставляемый Java. После этого вы можете использовать адаптеры или более специализированные итераторы. Остается то, что интерфейс, предоставляемый Java, является наиболее разумным для универсальных итераторов.
3. Это не вопрос случайного доступа: даже в прямом потоке я не вижу причины, по которой доступ к только что прочитанному значению и чтение следующего следует рассматривать как одну операцию. Но я предполагаю, что это довольно субъективно, и что нам придется согласиться, чтобы не согласиться :). Однако я хотел бы добавить, что мне кажется немного странным иметь
remove
метод в интерфейсе, который должен быть очень общим, когда удаление-это операция, которая возможна только для довольно ограниченного набора повторяющихся объектов.
Ответ №5:
Одна вещь, на которую я бы посмотрел, — это реализация Seq в clojure
Реализация базовых классов выполнена на Java, и доступен полный исходный код. Вопросы и ответы являются декораторами итераторов java (берут и реализуют интерфейсы итераторов java), но они также предоставляют свой собственный интерфейс, который может быть больше того, что вы хотите, или, по крайней мере, отправной точкой.
Ответ №6:
Я видел, что кто-то ссылался на коллекции Google, но никто не упоминал, что метод, который вы ищете, называется Iterators.peekingIterator().
Тем не менее, было бы лучше, если бы вы могли просто использовать листератор.
Ответ №7:
Как предположил Якаганович, вы, возможно, захотите ознакомиться с материалами google-коллекций. Определенно есть некоторая поддержка для некоторых вещей, которые вы хотите, например, подглядывать. Кроме того, как упоминали некоторые другие, реализация всех этих вещей для всех коллекций может быть опасной с точки зрения возможностей или производительности.
Ответ №8:
public class Iterazor<T> {
private Iterator<T> it;
public T top;
public Iterazor(Collection<T> co) {
this.it = co.iterator();
top = it.hasNext()? it.next(): null;
}
public void advance() {
top = it.hasNext()? it.next(): null;
}
}
// usage
for(Iterazor<MyObject> iz = new Iterazor<MyObject>(MyCollection);
iz.top!=null; iz.advance())
iz.top.doStuff();
}
Ответ №9:
Я никогда не сталкивался с проблемой, в которой мне нужно было заглянуть(); Итератор отлично сработал для меня. Мне интересно, как вы используете итераторы, которые, по вашему мнению, нуждаются в этой дополнительной функциональности.
Комментарии:
1. Я думаю, что самый распространенный случай-это какая-то отправка. Вы хотите прочитать первый элемент, чтобы увидеть, кто должен его обработать и отправить-если получателю нужна вся последовательность, хорошо просто передать это и не нужно также передавать удаленный объект.
2. Я это вижу. Просто в этом есть запах чего-то, что можно было бы сделать гораздо проще каким-то другим способом, например, посетителя или что-то в этом роде.
Ответ №10:
Похоже, вам было бы лучше использовать стек.
Ответ №11:
Коллекции Java были написаны таким образом, чтобы обеспечить минимальный набор полезных функций. Это очень хороший подход к коду, который должен быть реализован любым, кто реализует Java. Раздувание интерфейса с функциональностью, которая может быть полезной, может привести к значительному увеличению объема кода, а улучшения заметят лишь немногие. Если peek() и previous() были частью стандартного итератора, это означает, что каждый, кто пишет новый вид коллекции, должен реализовать его, разумно это или нет.
Итераторы также предназначены для работы с вещами, которые физически не могут вернуться назад, что делает невозможными как peek (), так и предыдущую ().