Есть ли "вычислительно" быстрый способ получить счетчик итератора?
int i = 0;
for ( ; some_iterator.hasNext() ; ++i ) some_iterator.next();
... кажется пустой тратой циклов процессора.
Есть ли "вычислительно" быстрый способ получить счетчик итератора?
int i = 0;
for ( ; some_iterator.hasNext() ; ++i ) some_iterator.next();
... кажется пустой тратой циклов процессора.
to provide an implementation-independent method for access, in which the user does not need to know whether the underlying implementation is some form of array or of linked list, and allows the user go through the collection without explicit indexing. penguin.ewu.edu/~trolfe/LinkedSort/Iterator.html
Ответы:
Если у вас только что есть итератор, то это то, что вам нужно сделать - он не знает, сколько элементов осталось для итерации, поэтому вы не можете запросить его для этого результата. Есть служебные методы, которые, похоже, делают это (например, Iterators.size()в Guava), но ниже они просто выполняют примерно ту же операцию.
Однако многие итераторы поступают из коллекций, размер которых часто можно запросить. И если это пользовательский класс, для которого вы получаете итератор, вы можете подумать о предоставлении метода size () для этого класса.
Короче говоря, в ситуации, когда у вас есть только итератор, лучшего способа нет, но гораздо чаще у вас есть доступ к базовой коллекции или объекту, из которого вы можете напрямую получить размер.
Iterators.size(...)(упомянутого в других комментариях ниже и в java-doc): «Возвращает количество элементов, оставшихся в итераторе. Итератор останется исчерпанным: его метод hasNext () вернет false». Это означает, что вы больше не можете использовать итератор после этого. Lists.newArrayList(some_iterator);может помочь.
Использование библиотеки Guava :
int size = Iterators.size(iterator);
Внутри он просто перебирает все элементы, так что это просто для удобства.
Ваш код выдаст вам исключение, когда вы дойдете до конца итератора. Вы могли сделать:
int i = 0;
while(iterator.hasNext()) {
i++;
iterator.next();
}
Если бы у вас был доступ к базовой коллекции, вы могли бы вызвать coll.size()...
ИЗМЕНИТЬ ОК, вы изменили ...
Вам всегда придется повторяться. Тем не менее, вы можете использовать Java 8, 9 для подсчета без явного цикла:
Iterable<Integer> newIterable = () -> iter;
long count = StreamSupport.stream(newIterable.spliterator(), false).count();
Вот тест:
public static void main(String[] args) throws IOException {
Iterator<Integer> iter = Arrays.asList(1, 2, 3, 4, 5).iterator();
Iterable<Integer> newIterable = () -> iter;
long count = StreamSupport.stream(newIterable.spliterator(), false).count();
System.out.println(count);
}
Это печатает:
5
Интересно, что вы можете распараллелить операцию подсчета здесь, изменив parallelфлаг этого вызова:
long count = StreamSupport.stream(newIterable.spliterator(), *true*).count();
Использование библиотеки Guava , другой вариант заключается в преобразовании Iterableк List.
List list = Lists.newArrayList(some_iterator);
int count = list.size();
Используйте это, если вам также нужно получить доступ к элементам итератора после получения его размера. Используя, Iterators.size()вы больше не можете получить доступ к повторяющимся элементам.
Если все, что у вас есть, - это итератор, то нет, "лучшего" пути нет. Если итератор поступает из коллекции, вы можете использовать размер.
Имейте в виду, что Iterator - это просто интерфейс для просмотра различных значений, у вас очень хорошо будет такой код, как этот
new Iterator<Long>() {
final Random r = new Random();
@Override
public boolean hasNext() {
return true;
}
@Override
public Long next() {
return r.nextLong();
}
@Override
public void remove() {
throw new IllegalArgumentException("Not implemented");
}
};
или
new Iterator<BigInteger>() {
BigInteger next = BigInteger.ZERO;
@Override
public boolean hasNext() {
return true;
}
@Override
public BigInteger next() {
BigInteger current = next;
next = next.add(BigInteger.ONE);
return current;
}
@Override
public void remove() {
throw new IllegalArgumentException("Not implemented");
}
};
Нет более эффективного способа, если все, что у вас есть, - это итератор. И если итератор можно использовать только один раз, то получить счетчик до того, как вы получите содержимое итератора ... проблематично.
Решение состоит в том, чтобы либо изменить ваше приложение, чтобы оно не нуждалось в подсчете, либо получить счет каким-либо другим способом. (Например, передатьCollection а не Iterator...)
Объект итератора содержит то же количество элементов, что и ваша коллекция.
List<E> a =...;
Iterator<E> i = a.iterator();
int size = a.size();//Because iterators size is equal to list a's size.
Но вместо того, чтобы получать размер итератора и перебирать индекс 0 до этого размера, лучше перебирать метод next () итератора.
a, а только i?