Идиома для парной итерации через отсортированную коллекцию
Есть ли идиома Java для парной итерации через элементы отсортированного Collection
? Под этим я подразумеваю, что каждая итерация имеет доступ к одному элементу коллекции и к следующему элементу коллекции?
Для отсортированных List
(и массивов) это можно сделать, используя индекс в коллекции:
final int n = list.size();
assert 2 <= n;
for (int i = 0; i < n - 1; ++i) {
final Thing thing1 = list.get(i);
final Thing thing2 = list.get(i+1);
operateOnAdjacentPair(thing1, thing2);
}
Но как насчет SortedSet
? (для SortedMap
вы можете использовать его entrySet()
, что эквивалентно случаю SortedSet
).
Итак, например, если ваш отсортированный набор содержал значения {1, 2, 3, 4}, итерации были бы для пар (1, 2), (2, 3), (3, 4), в этом порядке.
Ответы
Ответ 1
Вы можете просто реализовать его следующим образом (и применить аналогичную стратегию к другим коллекциям):
Iterator<Thing> iter = set.iterator();
Thing previous = iter.hasNext() ? iter.next() : null;
while (iter.hasNext()) {
final Thing current = iter.next();
operateOnAdjacentPair(previous, current);
previous = current;
}
Ответ 2
Iterator<Thing> thingerator = coll.iterator();
if (thingerator.hasNext()) {
Thing thing1 = thingerator.next();
while (thingerator.hasNext()) {
final Thing thing2 = thingerator.next();
doStuffToThings(thing1, thing2);
thing1 = thing2;
}
}
Ответ 3
Напишите реализацию Iterator, например. (просто сбрасывая верхнюю часть головы, поэтому код может не работать как есть)
public class PairwiseIterator<T> implements Iterator<List<T>> {
private final Iterator<T> elements;
private T last;
public PairwiseIterator(Collection<T> elements) {
this.elements = elements.iterator();
last = elements.hasNext() ? elements.next() : null;
}
@Override
public boolean hasNext() {
return elements.hasNext();
}
@Override
public List<T> next() {
List<T> result = ImmutableList.of(last, elements.next());
last = result.get(1);
return result;
}
@Override
public void remove() {
throw new UnsupportedOperationException("Remove not allowed with this iterator");
}
public static <U> Iterable<List<U>> iterable(final Collection<U> elements) {
return new Iterable() {
public Iterator<U> iterator() {
return new PairwiseIterator(elements);
}
}
}
}
У меня, вероятно, не все типы правильные, но метод "итерабельный" упрощает использование в конструкциях foreach:
for(List<String> pair : PairwiseIterator.iterable(orderedSetOfStrings)) {
// ... do what you need to ...
}
Ответ 4
Для Set
(и других неиндексируемых коллекций) вам нужно будет использовать их Iterator
, которые возвращаются iterator()
метод Collection
:
Iterator<Thing> iter = set.iterator();
Thing thing1 = iter.next(); // might want to check if this exists
while (iter.hasNext()) {
Thing thing2 = iter.next();
operateOnAdjacentPair(thing1, thing2);
thing1 = thing2;
}
Вы можете сделать то же самое для Map
s, используя Iterator
своих entrySet()
s.
Теперь, когда я лучше понимаю ваш вопрос, вы также можете попробовать следующее:
Iterator<Thing> iter1 = set.iterator(), iter2 = set.iterator();
if (iter2.hasNext())
iter2.next(); // burn first element
while (iter2.hasNext()) {
final Thing thing1 = iter1.next();
final Thing thing2 = iter2.next();
operateOnAdjacentPair(thing1, thing2);
}
Ответ 5
Guava предлагает PeekingIterator, который делает итерацию проще и безопаснее:
PeekingIterator<Thing> iter =
Iterators.peekingIterator(set.iterator());
while (iter.hasNext()) {
final Thing thing1 = iter.next();
if (iter.hasNext()) { // don't forget this one
operateOnAdjacentPair(thing1, iter.peek());
}
}
Когда код цикла становится более сложным, выгода может быть более очевидной.