Dapatkan ukuran Iterable di Java

90

Saya perlu mencari tahu jumlah elemen di dalam Iterabledi Java. Saya tahu saya bisa melakukan ini:

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

Saya juga bisa melakukan sesuatu seperti ini, karena saya tidak membutuhkan objek di Iterable lebih jauh:

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

Tolok ukur skala kecil tidak menunjukkan banyak perbedaan kinerja, ada komentar atau ide lain untuk masalah ini?

js84
sumber
Mengapa? Keduanya merupakan ide yang sangat buruk, karena keduanya adalah O (N). Anda harus mencoba untuk menghindari pengulangan koleksi dua kali.
pengguna207421
3
Yang kedua seharusnya tidak berfungsi. Anda tidak dapat menelepon remove()tanpa menelepon next()terlebih dahulu.
Louis Wasserman

Jawaban:

125

TL; DR: Gunakan metode utilitas Iterables.size(Iterable)dari pustaka Guava yang hebat .

Dari dua cuplikan kode Anda, Anda harus menggunakan yang pertama, karena yang kedua akan menghapus semua elemen dari values, jadi setelah itu kosong. Mengubah struktur data untuk kueri sederhana seperti ukurannya sangat tidak terduga.

Untuk performa, ini bergantung pada struktur data Anda. Jika itu misalnya pada kenyataannya an ArrayList, menghapus elemen dari awal (apa yang dilakukan metode kedua Anda) sangat lambat (menghitung ukurannya menjadi O (n * n) dan bukan O (n) sebagaimana mestinya).

Secara umum, jika ada peluang yang valuessebenarnya adalah a Collectiondan bukan hanya an Iterable, periksa ini dan panggil size()jika:

if (values instanceof Collection<?>) {
  return ((Collection<?>)values).size();
}
// use Iterator here...

Panggilan untuk size()kehendak biasanya jauh lebih cepat daripada menghitung jumlah elemen, dan trik ini adalah apa yang Iterables.size(Iterable)dari Guava lakukan untuk Anda.

Philipp Wendler
sumber
Dia mengatakan bahwa dia tidak tertarik dengan elemen dan tidak peduli jika mereka dihapus.
aioobe
Klarifikasi kecil: itu akan menghapus elemen darivalues
Miquel
1
Tetapi bagian lain dari kode mungkin masih menggunakan Iterable yang sama. Dan jika tidak sekarang, mungkin di masa depan.
Philipp Wendler
Saya sarankan agar jawaban Google Guava lebih menonjol. Tidak ada alasan untuk membuat orang menulis kode ini lagi, meskipun itu sepele.
Stephen Harrison
8
Jika Anda menggunakan Java 8, buat elemen Stream dan hitung di dalamnya sebagai: Stream.of (myIterable) .count ()
FrankBr
41

Jika Anda bekerja dengan java 8, Anda dapat menggunakan:

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

ini hanya akan berfungsi jika sumber yang dapat diulang memiliki ukuran yang ditentukan. Sebagian besar Spliterator untuk Koleksi akan melakukannya, tetapi Anda mungkin mengalami masalah jika itu berasal dari HashSetatau ResultSetmisalnya.

Anda dapat memeriksa javadoc di sini.

Jika Java 8 bukan merupakan pilihan , atau jika Anda tidak tahu dari mana iterable berasal, Anda dapat menggunakan pendekatan yang sama seperti 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;
    }
ArnaudR
sumber
1
Seseorang, berikan orang ini medali.
Pramesh Bajracharya
Tidak berhasil bagi saya untuk Sortir . Jawaban oleh New Bee bekerja untuk saya.
Sabir Khan
18

Ini mungkin agak terlambat, tetapi dapat membantu seseorang. Saya menemukan masalah serupa Iterabledalam basis kode saya dan solusinya adalah menggunakan for eachtanpa menelepon secara eksplisit values.iterator();.

int size = 0;
for(T value : values) {
   size++;
}
pilot
sumber
2
Bagi saya, ini adalah pendekatan intuitif, yang dapat saya hargai. Baru saja digunakan beberapa menit yang lalu. Terima kasih.
Matt Cremeens
2
Ya itu intuitif, tapi sayangnya Anda harus menekan peringatan yang tidak terpakai untuk nilai ...
Nils-o-mat
Terima kasih untuk solusi ini, ini memang mengembalikan ukuran objek. Satu-satunya kekurangan adalah jika Anda ingin mengulang lagi nanti melalui objek yang sama, maka pertama-tama Anda harus mengatur ulang.
Zsolti
6

Sebenarnya, Iterable tidak memiliki ukuran. Pikirkan struktur data seperti siklus.

Dan pikirkan tentang mengikuti contoh Iterable, Tanpa ukuran:

    new Iterable(){

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

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

                @Override
                public Object next() {
                    return fetchDataFromExternalSystem();
                }};
        }};
卢 声 远 Shengyuan Lu
sumber
6

Anda dapat mentransmisikan iterable Anda ke daftar lalu menggunakan .size () di atasnya.

Lists.newArrayList(iterable).size();

Demi kejelasan, metode di atas akan membutuhkan impor berikut:

import com.google.common.collect.Lists;
tipu
sumber
3
Daftarnya adalah kelas Guava
Paul Jackson
2

Saya akan pergi karena it.next()alasan sederhana yang next()dijamin akan diterapkan, sedangkan remove()operasi opsional.

E next()

Mengembalikan elemen berikutnya dalam iterasi.

void remove()

Menghapus dari koleksi yang mendasari elemen terakhir yang dikembalikan oleh iterator (operasi opsional) .

aioobe
sumber
Meskipun jawaban ini benar secara teknis , namun cukup menyesatkan. Memanggil removetelah dicatat sebagai cara yang salah untuk menghitung elemen Iterator, jadi tidak masalah apakah hal yang tidak akan kita lakukan diterapkan atau tidak.
Stephen Harrison
1
Bagian mana dari jawaban itu yang menyesatkan? Dan di bawah keadaan di mana remove yang dilaksanakan, mengapa itu akan salah untuk menggunakannya? Btw, downvote biasanya digunakan untuk jawaban yang salah atau jawaban yang memberikan nasihat buruk. Saya tidak dapat melihat bagaimana jawaban ini memenuhi syarat untuk semua itu.
aioobe
2

java 8 ke atas

StreamSupport.stream(data.spliterator(), false).count();
Bee baru
sumber
0

Bagi saya, ini hanyalah metode yang berbeda. Yang pertama membiarkan objek yang Anda iterasi tidak berubah, sedangkan detik membiarkannya kosong. Pertanyaannya adalah apa yang ingin Anda lakukan. Kompleksitas penghapusan didasarkan pada implementasi objek iterable Anda. Jika Anda menggunakan Koleksi - dapatkan saja ukuran seperti yang diusulkan oleh Kazekage Gaara - biasanya ini merupakan performa pendekatan terbaik yang bijaksana.

Mark Bramnik
sumber
-2

Mengapa Anda tidak menggunakan size()metode pada Anda Collectionuntuk mendapatkan jumlah elemen?

Iterator hanya dimaksudkan untuk mengulang, tidak ada yang lain.

Kazekage Gaara
sumber
4
Siapa bilang dia menggunakan koleksi? :-)
aioobe
Anda menggunakan iterator, yang mendukung penghapusan elemen. Jika Anda mengambil ukuran sebelum memasuki loop iterator, Anda harus berhati-hati dengan penghapusan Anda dan memperbarui nilainya.
Miquel
1
Contoh mengapa dia mungkin tidak memiliki koleksi untuk dilihat ukurannya: dia bisa memanggil metode API pihak ketiga yang hanya mengembalikan Iterable.
SteveT
2
Jawaban ini membingungkan Iteratordan Iterable. Lihat jawaban yang dipilih untuk cara yang benar.
Stephen Harrison
-4

Alih-alih menggunakan loop dan menghitung setiap elemen atau menggunakan dan pustaka pihak ketiga, kita cukup mengetikkan iterable di ArrayList dan mendapatkan ukurannya.

((ArrayList) iterable).size();
fatimasajjad
sumber
3
Tidak semua iterable dapat di-cast ke ArrayList tho
Alexander Mills