Подтвердить что ты не робот

Получить размер Iterable в Java

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

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

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

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

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

4b9b3361

Ответ 1

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

Из двух ваших фрагментов кода вы должны использовать первый, потому что второй будет удалять все элементы из 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) Guava для вас.

Ответ 2

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

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

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

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

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

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

Ответ 3

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

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

Ответ 4

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

И подумайте о следующем экземпляре Iterable, No size:

    new Iterable(){

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

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

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

Ответ 5

Вы можете перечислить итерацию в список, а затем использовать .size().

Lists.newArrayList(iterable).size();

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

import com.google.common.collect.Lists;

Ответ 6

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

E next()

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

void remove()

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

Ответ 7

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

Ответ 8

Java 8 и выше

StreamSupport.stream(data.spliterator(), false).count();

Ответ 9

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

Iterator предназначен только для итерации, ничего другого.

Ответ 10

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

((ArrayList) iterable).size();