Який найкращий спосіб отримати кількість / довжину / розмір ітератора?


96

Чи існує «обчислювально» швидкий спосіб отримати підрахунок ітератора?

int i = 0;
for ( ; some_iterator.hasNext() ; ++i ) some_iterator.next();

... здається марна трата циклів процесора.


2
Ітератор не обов'язково відповідає чомусь із "підрахунком" ...
Олівер Чарлсворт

Ітератори - це те, що вони є; здійснити ітерацію до наступного об’єкта колекції (це може бути щось на зразок набору, масиву тощо) Чому їм потрібно вказувати розмір, коли їм байдуже, для чого вони намагаються зробити ітерацію? 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
ecle

Відповіді:


67

Якщо ви щойно отримали ітератор, то це те, що вам доведеться зробити - він не знає, скільки елементів йому залишилося переглядати, тому ви не можете запитати його для цього результату. Існують утилітні методи, які, здається, роблять це (наприклад, Iterators.size()у Гуаві), але внизу вони виконують приблизно однакову операцію.

Однак багато ітераторів походять із колекцій, які часто можна задати щодо їх розміру. І якщо це користувальницький клас, для якого ви отримуєте ітератор, ви можете подивитися, щоб надати метод size () для цього класу.

Коротше кажучи, в ситуації , коли ви тільки маєте ітератора , то немає кращого способу, але набагато частіше , ніж ви не маєте доступ до основної колекції або об'єкту , з якого ви можете бути в змозі отримати розмір безпосередньо.


Остерігайтеся побічного ефекту Iterators.size(...)(згаданого в інших коментарях нижче та в java-doc): "Повертає кількість елементів, що залишилися в ітераторі. Ітератор залишиться вичерпаним: його метод hasNext () поверне false." Це означає, що ви більше не можете використовувати Ітератор. Lists.newArrayList(some_iterator);може допомогти.
MichaelCkr

91

Використання бібліотеки гуави :

int size = Iterators.size(iterator);

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


8
Це дуже елегантно. Тільки пам’ятайте, що ви споживаєте свій ітератор (тобто ітератор згодом буде порожнім)
lolski,

1
Це не "обчислювально швидко", це зручний метод, який має небажаний побічний ефект від споживання ітератора.
Зак,

Ви можете пояснити, як це працює? @Andrejs List <Tuple2 <String, Integer >> wordCountsWithGroupByKey = wordsPairRdd.groupByKey () .mapValues ​​(intIterable -> Iterables.size (intIterable)). Collect (); System.out.println ("wordCountsWithGroupByKey:" + wordCountsWithGroupByKey); "Iterables.size (intIterable)?
Верма,

15

Ваш код дасть вам виняток, коли ви дійдете до кінця ітератора. Ви можете зробити:

int i = 0;
while(iterator.hasNext()) {
    i++;
    iterator.next();
}

Якби у вас був доступ до базової колекції, ви могли б зателефонувати coll.size()...

EDIT OK, ви внесли зміни ...


наскільки це ефективно? що робити, якщо ітератор схожий на мільйон значень?
Micro

4
@Micro технічно ітератор може бути нескінченним - у цьому випадку цикл буде тривати вічно.
assylias

11

Вам завжди доведеться повторити. Тим не менш, ви можете використовувати 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();

8

Використовуючи бібліотеку Гуави , інший варіант - перетворити Iterableфайл на List.

List list = Lists.newArrayList(some_iterator);
int count = list.size();

Використовуйте це, якщо вам також потрібно отримати доступ до елементів ітератора після отримання його розміру. Використовуючи, Iterators.size()ви більше не можете отримати доступ до повторюваних елементів.


2
@LoveToCode Менш ефективний, ніж приклад з оригінального питання
Зимовий

2
Звичайно, створення нового об’єкта з усіма елементами відбувається повільніше, ніж просто ітерація та відкидання. ІМХО, це рішення є однолінійним, що покращує читабельність коду. Я багато використовую його для колекцій з невеликою кількістю елементів (до 1000) або коли швидкість не є проблемою.
ташушка

7

Якщо у вас є лише ітератор, то ні, немає "кращого" шляху. Якщо ітератор походить із колекції, ви можете зробити це за розміром.

Майте на увазі, що 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");
        }
    }; 

4

Немає більш ефективного способу, якщо все, що у вас є - це ітератор. І якщо ітератор можна використовувати лише один раз, то отримання підрахунку, перш ніж отримати вміст ітератора, є ... проблематичним.

Рішення полягає або в тому, щоб змінити свою програму так, щоб вона не потребувала підрахунку, або отримати підрахунок якимось іншим способом. (Наприклад, передайте, Collectionа не Iterator...)


0

для Java 8 ви можете використовувати,

public static int getIteratorSize(Iterator iterator){
        AtomicInteger count = new AtomicInteger(0);
        iterator.forEachRemaining(element -> {
            count.incrementAndGet();
        });
        return count.get();
    }

-5

Об'єкт 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?
Tvde1,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.