イテレータの数/長さ/サイズを取得する最良の方法は何ですか?


96

イテレータの数を取得する「計算的に」迅速な方法はありますか?

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

... CPUサイクルの無駄のようです。


2
イテレータは必ずしも「カウント」を持つものに対応するとは限りません...
オリバーチャールズワース2012年

イテレータはそれらが何であるかです。コレクションの次のオブジェクト(これは、セット、配列などのいずれかになる可能性があります)を反復処理します。なぜ、何を反復しようとしているのかを気にしないときに、サイズを通知する必要があるのはなぜですか。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
エクル

回答:


67

イテレータを取得しただけの場合は、それを行う必要があります。反復するために残っているアイテムの数がわからないため、その結果をクエリすることはできません。これを行うと思われるユーティリティメソッド(Iterators.size()Guava など)がありますが、その下では、ほぼ同じ操作を実行しています。

ただし、多くのイテレータはコレクションから取得され、コレクションのサイズを問い合わせることができます。イテレータを取得するユーザー作成クラスの場合は、そのクラスでsize()メソッドを提供することを検討できます。

簡単に言えば、イテレータしかない状況では、より良い方法はありませんが、多くの場合、サイズを直接取得できる基になるコレクションまたはオブジェクトにアクセスできます。


91

Guavaライブラリの使用:

int size = Iterators.size(iterator);

内部的には、すべての要素を反復処理するだけなので、便宜上です。


7
これはとてもエレガントです。イテレータを消費していることを覚えておいてください(つまり、イテレータは後で空になります)
lolski

1
これは「計算上迅速」ではなく、イテレータを消費するという望ましくない副作用がある便利な方法です。
ザック

これがどのように機能するか説明していただけますか?@Andrejs List <Tuple2 <String、Integer >> wordCountsWithGroupByKey = wordsPairRdd.groupByKey().mapValues(intIterable-> Iterables.size(intIterable))。collect(); System.out.println( "wordCountsWithGroupByKey:" + wordCountsWithGroupByKey); "Iterables.size(intIterable)?
Aditya Verma

15

イテレータの最後に到達すると、コードで例外が発生します。あなたはできる:

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

基になるコレクションにアクセスできる場合は、呼び出すことができますcoll.size()...

あなたが修正した編集 OK ...


これはどのくらい効率的ですか?イテレータが100万個の値のような場合はどうなりますか?
Micro

4
@Micro技術的にはイテレータは無限である可能性があります-その場合、ループは永久に続きます。
アッシリア

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();

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");
        }
    }; 

7

Guavaライブラリを使用する別のオプションは、をに変換するIterableことListです。

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

サイズを取得した後でイテレータの要素にアクセスする必要がある場合は、これを使用します。を使用Iterators.size()すると、反復された要素にアクセスできなくなります。


2
@LoveToCode元の質問の例よりも効率が悪い
Winter

2
もちろん、すべての要素を持つ新しいオブジェクトの作成は、単に繰り返して破棄するよりも時間がかかります。私見、このソリューションはコードを読みやすくするためのワンライナーです。要素の少ないコレクション(最大1000)の場合、または速度が問題にならない場合に、これを頻繁に使用します。
tashuhka 2016

4

イテレータしかなければ、これ以上効率的な方法はありません。イテレータを1回しか使用できない場合、イテレータのコンテンツを取得する前にカウントを取得するのは問題があります。

解決策は、カウントを必要としないようにアプリケーションを変更するか、他の手段でカウントを取得することです。(たとえば、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

イテレータオブジェクトには、コレクションに含まれていたのと同じ数の要素が含まれます。

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 2017
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.