Apa cara terbaik untuk mendapatkan jumlah / panjang / ukuran sebuah iterator?

96

Apakah ada cara cepat "secara komputasi" untuk menghitung iterator?

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

... sepertinya membuang-buang siklus CPU.

Zak
sumber
2
Sebuah iterator tidak selalu sesuai dengan sesuatu dengan "hitungan" ...
Oliver Charlesworth
Iterator adalah apa adanya; untuk beralih ke objek koleksi berikutnya (bisa berupa apa saja seperti set, array, dll.) Mengapa mereka perlu memberi tahu ukuran ketika mereka tidak peduli untuk apa mereka mencoba mengulang? 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

Jawaban:

67

Jika Anda baru saja mendapatkan iterator maka itulah yang harus Anda lakukan - iterator tidak tahu berapa banyak item yang tersisa untuk diiterasi, jadi Anda tidak dapat menanyakannya untuk hasil itu. Ada metode utilitas yang tampaknya melakukan ini (seperti Iterators.size()di Guava), tetapi di bawahnya mereka hanya melakukan operasi yang kira-kira sama.

Namun, banyak iterator berasal dari koleksi, yang sering kali dapat Anda kueri untuk ukurannya. Dan jika itu adalah kelas buatan pengguna yang Anda peroleh iteratornya, Anda bisa memberikan metode size () pada kelas itu.

Singkatnya, dalam situasi di mana Anda hanya memiliki iterator maka tidak ada cara yang lebih baik, tetapi lebih sering daripada tidak Anda memiliki akses ke koleksi atau objek yang mendasarinya dari mana Anda mungkin bisa mendapatkan ukurannya secara langsung.

Michael Berry
sumber
Waspadai efek samping Iterators.size(...)(disebutkan dalam komentar lain di bawah dan di java-doc): "Mengembalikan jumlah elemen yang tersisa di iterator. Iterator akan dibiarkan habis: metode hasNext () akan mengembalikan false." Artinya, Anda tidak dapat menggunakan Iterator lagi setelahnya. Lists.newArrayList(some_iterator);mungkin membantu.
MichaelCkr
91

Menggunakan pustaka Guava :

int size = Iterators.size(iterator);

Secara internal itu hanya mengulangi semua elemen jadi itu hanya untuk kenyamanan.

Andrejs
sumber
8
Ini sangat elegan. Ingatlah bahwa Anda menggunakan iterator Anda (yaitu, iterator akan kosong setelahnya)
lolski
1
Ini bukan "komputasi cepat", ini adalah metode praktis yang memiliki efek samping yang tidak diinginkan dari penggunaan iterator.
Zak
Bisakah Anda menjelaskan bagaimana cara kerjanya? @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

Kode Anda akan memberi Anda pengecualian saat Anda mencapai akhir iterator. Anda bisa melakukan:

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

Jika Anda memiliki akses ke koleksi yang mendasarinya, Anda akan dapat menghubungi coll.size()...

EDIT OK, Anda telah mengubah ...

assylias
sumber
seberapa efisien ini? bagaimana jika iterator seperti sejuta nilai?
Mikro
4
@Micro secara teknis, iterator bisa jadi tidak terbatas - dalam hal ini loop akan berlangsung selamanya.
assylias
11

Anda harus selalu mengulang. Namun Anda bisa menggunakan Java 8, 9 untuk melakukan penghitungan tanpa melakukan perulangan secara eksplisit:

Iterable<Integer> newIterable = () -> iter;
long count = StreamSupport.stream(newIterable.spliterator(), false).count();

Ini tesnya:

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

Ini mencetak:

5

Cukup menarik Anda dapat memparalelkan operasi penghitungan di sini dengan mengubah parallelbendera pada panggilan ini:

long count = StreamSupport.stream(newIterable.spliterator(), *true*).count();
gil.fernandes
sumber
8

Menggunakan pustaka Guava , opsi lain adalah mengonversi Iterableke a List.

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

Gunakan ini jika Anda juga perlu mengakses elemen iterator setelah mendapatkan ukurannya. Dengan menggunakan Iterators.size()Anda tidak lagi dapat mengakses elemen iterasi.

tashuhka
sumber
2
@LoveToCode Kurang efisien daripada contoh pada pertanyaan awal
Musim Dingin
2
Tentu, membuat objek baru dengan semua elemen lebih lambat daripada hanya mengulang dan membuang. IMHO, solusi ini adalah satu baris yang meningkatkan keterbacaan kode. Saya sering menggunakannya untuk koleksi dengan sedikit elemen (hingga 1000) atau saat kecepatan tidak menjadi masalah.
tashuhka
7

Jika yang Anda miliki hanyalah iterator, maka tidak, tidak ada cara yang "lebih baik". Jika iterator berasal dari koleksi, Anda bisa menggunakannya untuk ukuran.

Perlu diingat bahwa Iterator hanyalah antarmuka untuk melintasi nilai yang berbeda, Anda akan memiliki kode seperti ini

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

atau

    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");
        }
    }; 
Roger Lindsjö
sumber
4

Tidak ada cara yang lebih efisien, jika yang Anda miliki hanyalah iterator. Dan jika iterator hanya dapat digunakan satu kali, maka menghitung sebelum Anda mendapatkan konten iterator adalah ... bermasalah.

Solusinya adalah mengubah aplikasi Anda sehingga tidak memerlukan penghitungan, atau untuk mendapatkan penghitungan dengan cara lain. (Misalnya, berikan lulus Collectiondaripada Iterator...)

Stephen C
sumber
0

untuk Java 8 Anda bisa menggunakan,

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

objek iterator berisi jumlah elemen yang sama dengan koleksi Anda.

List<E> a =...;
Iterator<E> i = a.iterator();
int size = a.size();//Because iterators size is equal to list a's size.

Tetapi daripada mendapatkan ukuran iterator dan melakukan iterasi melalui indeks 0 ke ukuran tersebut, lebih baik melakukan iterasi melalui metode next () dari iterator.

Chandra Sekhar
sumber
Bagaimana jika kita tidak punya a, tapi hanya i?
Tvde1