Отримати розмір Iterable в Java


89

Мені потрібно з’ясувати кількість елементів Iterableу Java. Я знаю, що можу це зробити:

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

Я також міг би зробити щось подібне, тому що мені більше не потрібні об'єкти в Iterable:

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

Невеликий орієнтир не показав великої різниці у продуктивності, коментарів чи інших ідей щодо цієї проблеми?


Чому? І те, і інше є насправді поганою ідеєю, оскільки обидва є O (N). Спробуйте уникати повторення колекції двічі.
Маркіз Лорнський

3
Ваш другий навіть не повинен працювати. Ви не можете зателефонувати, remove()не зателефонувавши next()заздалегідь.
Louis Wasserman

Відповіді:


123

TL; DR: Використовуйте метод корисності Iterables.size(Iterable)великої бібліотеки Гуави .

З ваших двох фрагментів коду ви повинні використовувати перший, оскільки другий видалить усі елементи values, тому він порожній. Зміна структури даних для такого простого запиту, як його розмір, є дуже несподіваною.

Для продуктивності це залежить від вашої структури даних. Якщо це, наприклад, насправді an 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)з гуави робить для вас.


Він каже, що його елементи не цікавлять і йому все одно, якщо їх видалити.
aioobe

Невелике роз'яснення: це видалить елементи зvalues
Мікель

1
Але інші частини коду все ще можуть використовувати той самий Iterable. І якщо не зараз, можливо, в майбутньому.
Філіп Вендлер

Я пропоную зробити відповідь Google Guava більш помітною. Немає жодної причини змушувати людей писати цей код знову, хоча він і тривіальний.
Стівен Гаррісон,

8
Якщо ви використовуєте Java 8, створіть Stream і підрахуйте в ньому елемент як: Stream.of (myIterable) .count ()
FrankBr

41

Якщо ви працюєте з java 8, ви можете використовувати:

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

це спрацює лише в тому випадку, якщо ітерабельне джерело має визначений розмір. Більшість Сплітераторів для колекцій будуть, але у вас можуть виникнути проблеми, якщо вони походять від HashSetабо, ResultSetнаприклад.

Ви можете перевірити javadoc тут.

Якщо Java 8 не є опцією або якщо ви не знаєте, звідки береться ітерація, ви можете використовувати той самий підхід, що і гуава:

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

1
Хтось, вручте цьому хлопцеві медаль.
Прамеш Байрачар'я,

Для мене це не працює для Сортування . Відповідь від New Bee працює для мене.
Сабір-хан

18

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

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

2
Для мене це інтуїтивний підхід, який я можу оцінити. Просто використовував його кілька хвилин тому. Дякую.
Matt Cremeens,

2
Так, це інтуїтивно, але, на жаль, вам потрібно придушити невикористане попередження щодо цінності ...
Nils-o-mat

Дякуємо за це рішення, воно справді повертає розмір об'єкта. Єдина зворотна сторона полягає в тому, що якщо ви хочете пізніше повторити цей самий об’єкт, то спочатку слід якось скинути його.
Zsolti

7

Ви можете додати свій ітерабель до списку, а потім використовувати .size () на ньому.

Lists.newArrayList(iterable).size();

Для наочності для наведеного методу буде потрібно наступне імпортування:

import com.google.common.collect.Lists;

2
Списки - це клас гуави
Пол Джексон,

6

Власне кажучи, 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();
                }};
        }};

2

Я б пішов з it.next()тієї простої причини, яка next()гарантовано буде реалізована, хоча remove()це необов’язкова операція.

E next()

Повертає наступний елемент в ітерації.

void remove()

Видаляє з базової колекції останній елемент, повернутий ітератором (необов’язкова операція) .


Хоча ця відповідь є технічно правильною, вона вводить в оману. Виклик removeвже було зазначено як неправильний спосіб підрахунку елементів an Iterator, тому насправді не має значення, реалізовано чи ні те, що ми не збираємось робити.
Стівен Гаррісон,

1
Яка саме частина відповіді вводить в оману? І за таких обставин , коли remove буде реалізований, чому це було б неправильно використовувати його? До речі, голоси проти є типовими для використання для неправильних відповідей або відповідей, які дають погані поради. Я не можу зрозуміти, як ця відповідь відповідає будь-якому із цього.
aioobe


0

Як на мене, це просто різні методи. Перший залишає об’єкт, для якого ви ітерацію, незмінним, а секунди залишає його порожнім. Питання в тому, що ти хочеш робити. Складність видалення заснована на реалізації вашого ітерабельного об'єкта. Якщо ви використовуєте колекції - просто отримайте розмір, запропонований Kazekage Gaara, - як правило, це найкращий підхід.


-2

Чому б вам просто не використовувати size()метод на своєму, Collectionщоб отримати кількість елементів?

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


4
Хто каже, що користується колекцією? :-)
aioobe

Ви використовуєте ітератор, який підтримує видалення елементів. Якщо ви підбираєте розмір перед входом у цикл ітератора, вам слід бути обережним із видаленнями та відповідно оновити значення.
Miquel

1
Приклад того, чому у нього може не бути колекції для розгляду розміру: він може викликати сторонній метод API, який повертає лише Iterable.
SteveT

2
Ця відповідь бентежить Iteratorі Iterable. Правильний шлях див. У відповіді, проголосованому голосом.
Стівен Гаррісон,

-4

Замість того, щоб використовувати цикли та підраховувати кожен елемент, або за допомогою сторонніх бібліотек, ми можемо просто ввести в ідентифікатор ітерабель в ArrayList і отримати його розмір.

((ArrayList) iterable).size();

3
Не всі ітерабелі можна передати на ArrayList
Олександр Міллс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.