Сделать размер итератор в Java


мне нужно выяснить количество элементов в Iterable в Java. Я знаю, что могу сделать это:

Iterable values = ...
it = values.iterator();
while (it.hasNext()) {
  it.next();
  sum++;
}

я мог бы также сделать что-то вроде этого, потому что мне не нужны объекты в Iterable дальше:

it = values.iterator();
while (it.hasNext()) {
  it.remove();
  sum++;
}

небольшой масштабный тест не показал большой разницы в производительности, никаких комментариев или других идей для этой проблемы?

9 61

9 ответов:

TL; DR: используйте метод утилиты Iterables.size(Iterable) великого гуавы библиотека.

из двух фрагментов кода, Вы должны использовать первый, потому что второй будет удалить все элементы из values, Так что после этого он пуст. Изменение структуры данных для простого запроса, такого как его размер, очень неожиданно.

для исполнения, это зависит от вашей структуры данных. Если это, например, на самом деле ArrayList, удаление элементов с самого начала (что делает ваш второй метод) очень медленно(вычисление размера становится O(n*n) вместо O (n), как это должно быть).

в общем, если есть шанс, что values на самом деле Collection и не только Iterable, проверьте это и называют size() в случае:

if (values instanceof Collection<?>) {
  return ((Collection<?>)values).size();
}
// use Iterator here...

вызов size() обычно будет намного быстрее, чем подсчет количества элементов, и этот трюк именно то, что Iterables.size(Iterable) of гуавы делает для вас.

если вы работаете с Java 8 вы можете использовать:

Iterable values = ...
long size = values.spliterator().getExactSizeIfKnown();

он будет работать только в том случае, если итерационный источник имеет определенный размер. Большинство разделителей для коллекций будет, но у вас могут возникнуть проблемы, если он исходит от HashSetили ResultSetнапример.

вы можете проверить javadoc здесь.

если Java 8 не является опцией, или если вы не знаете, откуда берется iterable, вы можете использовать тот же подход, что и гуава:

  if (iterable instanceof Collection) {
        return ((Collection<?>) iterable).size();
    } else {
        int count = 0;
        Iterator iterator = iterable.iterator();
        while(iterator.hasNext()) {
            iterator.next();
            count++;
        }
        return count;
    }

это, возможно, немного поздно, но может помочь кому-то. Я сталкиваюсь с подобной проблемой с Iterable в моей кодовой базе и решение было использовать for each без явного вызова values.iterator();.

int size = 0;
for(T value : values) {
   size++;
}

строго говоря, Iterable не имеет размера. Подумайте о структуре данных, как цикл.

и думать о следующем экземпляре повторяемое, размер:

    new Iterable(){

        @Override public Iterator iterator() {
            return new Iterator(){

                @Override
                public boolean hasNext() {
                    return isExternalSystemAvailble();
                }

                @Override
                public Object next() {
                    return fetchDataFromExternalSystem();
                }};
        }};

вы можете привести свой iterable к списку, а затем использовать .размер() на нем.

Lists.newArrayList(iterable).size();

для ясности вышеописанный метод потребует следующего импорта:

import com.google.common.collect.Lists;

Я бы пошел на it.next() по той простой причине, что next() гарантированно будет реализован, в то время как remove() является необязательной операцией.

E next()

возвращает следующий элемент в итерации.

void remove()

удаляет из базовой коллекции последний элемент, возвращенный iterator (дополнительная работа).

Как по мне, это просто разные методы. Первый оставляет объект, который вы повторяете, неизменным, в то время как секунды оставляют его пустым. Вопрос в том, что вы хотите сделать. Сложность удаления основана на реализации вашего итерационного объекта. Если вы используете коллекции-просто получите размер, как было предложено Kazekage Gaara-обычно это лучший подход к производительности.

вместо использования циклов и подсчета каждого элемента или использования и сторонней библиотеки мы можем просто типизировать итерацию в ArrayList и получить ее размер.

((ArrayList) iterable).size();

почему бы вам просто не использовать size() метод Collection получить количество элементов?

Iterator просто предназначен для итерации, ничего больше.