Konversi Java Array ke Iterable

150

Saya memiliki Array primitif, misalnya untuk int, int [] foo. Mungkin yang berukuran kecil, atau tidak.

int foo[] = {1,2,3,4,5,6,7,8,9,0};

Apa cara terbaik untuk membuatnya Iterable<Integer>?

Iterable<Integer> fooBar = convert(foo);

Catatan:

Tolong jangan menjawab menggunakan loop (kecuali Anda dapat memberikan penjelasan yang baik tentang bagaimana kompiler melakukan sesuatu yang pintar tentang mereka?)

Perhatikan juga itu

int a[] = {1,2,3};
List<Integer> l = Arrays.asList(a);

Bahkan tidak akan mengkompilasi

Type mismatch: cannot convert from List<int[]> to List<Integer>

Juga periksa Mengapa array tidak dapat ditugaskan untuk Iterable? sebelum menjawab.

Juga, jika Anda menggunakan beberapa perpustakaan (mis. Jambu), tolong jelaskan mengapa ini adalah yang terbaik. (Karena dari Google bukan jawaban lengkap: P)

Terakhir, karena tampaknya ada pekerjaan rumah tentang itu, hindari memposting kode pekerjaan rumah.

ntg
sumber
kemungkinan duplikat Iterator untuk array
NPE
Tambahkan mereka ke LinkedList lalu kembalikan iterator dari Set itu.

Jawaban:

117
Integer foo[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };

List<Integer> list = Arrays.asList(foo);
// or
Iterable<Integer> iterable = Arrays.asList(foo);

Meskipun Anda perlu menggunakan Integerarray (bukan intarray) agar ini berfungsi.

Untuk primitif, Anda dapat menggunakan jambu biji:

Iterable<Integer> fooBar = Ints.asList(foo);
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>15.0</version>
    <type>jar</type>
</dependency>

Untuk Java8: (dari jawaban Jin Kwon)

final int[] arr = {1, 2, 3};
final PrimitiveIterator.OfInt i1 = Arrays.stream(arr).iterator();
final PrimitiveIterator.OfInt i2 = IntStream.of(arr).iterator();
final Iterator<Integer> i3 = IntStream.of(arr).boxed().iterator();
fmucar
sumber
10
Dua catatan: 1) dia punya int, bukan Integer2) Listsudah Iterablejadi baris ketiga tidak ada gunanya.
maksimov
1
dia butuh Iterable itu sebabnya ada baris ketiga.
fmucar
5
Baris ke-2 dan ke-3 adalah opsi yang saya katakan :)
fmucar
1
Ini bukan bagian dari pekerjaan rumah, saya hanya mencoba untuk menghindari duplikasi kode untuk fungsi debugging memproses isi dari array atau daftar ... Sambil melihat-lihat saya memang menemukan Arrays.asList (..) ;, tapi setidaknya Eclipse tampaknya berpikir itu tidak akan melakukan apa yang saya inginkan (misalnya, itu menyimpulkan hasil dari Array.asList (foo) sebagai Daftar <int []>, bukan Daftar <Integer> ...) Saya menemukan ini cukup menarik untuk sebuah pertanyaan ... (-Kritik komentar di bagian penyebab batas-)
ntg
1
Secara umum orang bisa memikirkan banyak cara untuk melakukannya, tapi saya bertanya-tanya apa yang TERBAIK (misalnya loop akan imho menjadi bubur lebih lambat dibandingkan dengan ... {well, masalahnya adalah saya tidak bisa memikirkan apa pun !: )}) Juga periksa stackoverflow.com/questions/1160081/... untuk diskusi tentang mengapa, pertanyaan saya bukanlah mengapa, tapi bagaimana, dan jenis wadah apa yang terbaik (mengapa ArrayList? Sebenarnya, saya dapat membayangkan beberapa AbstractList bungkus menggunakan Generics .., Mungkin tergantung pada ukuran ...)
ntg
44

hanya 2 sen saya:

final int a[] = {1,2,3};

java.lang.Iterable<Integer> aIterable=new Iterable<Integer>() {

    public Iterator<Integer> iterator() {
       return new Iterator<Integer>() {
            private int pos=0;

            public boolean hasNext() {
               return a.length>pos;
            }

            public Integer next() {
               return a[pos++];
            }

            public void remove() {
                throw new UnsupportedOperationException("Cannot remove an element of an array.");
            }
        };
    }
};
Joerg Ruethschilling
sumber
9
hapus () bukan neccessary di java 8, karena ini adalah metode default yang melempar UnsupportedOperationException. Hanya jika Anda ingin memberikan pesan penjelasan yang lebih baik.
Alex
+1 Saya melakukan sesuatu yang mirip untuk membuat Iterator<Character>dari String. Menerapkan milik Anda sendiri Iteratorsepertinya satu-satunya cara untuk menghindari perulangan yang tidak perlu melalui semua nilai untuk mengkonversi dari tipe objek ke tipe primitif (misalnya melalui Guava Ints.asList()), hanya untuk bisa mendapatkan Iteratordari Listyang telah dibuat.
spaaarky21
2
Anda benar, Alex. Metode default ditambahkan ke Java 8. Pada 2013 saya menambahkan kode kuno ini di sini.
Joerg Ruethschilling
29

Dengan Java 8, Anda dapat melakukan ini.

final int[] arr = {1, 2, 3};
final PrimitiveIterator.OfInt i1 = Arrays.stream(arr).iterator();
final PrimitiveIterator.OfInt i2 = IntStream.of(arr).iterator();
final Iterator<Integer> i3 = IntStream.of(arr).boxed().iterator();
Jin Kwon
sumber
20

Guava menyediakan adaptor yang Anda inginkan sebagai Int.asList () . Ada yang setara untuk setiap tipe primitif di kelas terkait, misalnya, Booleansuntuk boolean, dll.

int foo[] = {1,2,3,4,5,6,7,8,9,0};
Iterable<Integer> fooBar = Ints.asList(foo);
for(Integer i : fooBar) {
    System.out.println(i);
}

Saran di atas untuk digunakan Arrays.asListtidak akan berfungsi, bahkan jika mereka dikompilasi karena Anda mendapatkan Iterator<int[]>daripada Iterator<Integer>. Apa yang terjadi adalah bahwa alih-alih membuat daftar yang didukung oleh array Anda, Anda membuat daftar 1-elemen array, yang berisi array Anda.

BeeOnRope
sumber
hanya sebuah catatan: tautannya tidak berfungsi lagi. Tautan Github: github.com/google/guava/blob/master/guava/src/com/google/common/…
Orangle
Terima kasih @Passi, diperbaiki (tampaknya tidak dapat menemukan cara yang didukung Google untuk menautkan ke javadoc lagi jadi saya ditautkan ke sumber yang Anda berikan).
BeeOnRope
8

Saya memiliki masalah yang sama dan menyelesaikannya seperti ini:

final YourType[] yourArray = ...;
return new Iterable<YourType>() {
  public Iterator<YourType> iterator() {
     return Iterators.forArray(yourArray);   // Iterators is a Google guava utility
  }
}

Iterator itu sendiri malas UnmodifiableIteratortapi itulah yang saya butuhkan.

mindas
sumber
7

Di Java 8 atau lebih baru, Iterableadalah antarmuka fungsional yang kembali Iterator. Jadi kamu bisa melakukan ini.

int[] array = {1, 2, 3};
Iterable<Integer> iterable = () -> Arrays.stream(array).iterator();
for (int i : iterable)
    System.out.println(i);

->

1
2
3
saka1029
sumber
3

Pertama-tama, saya hanya bisa setuju bahwa Arrays.asList(T...)ini jelas merupakan solusi terbaik untuk tipe Wrapper atau array dengan tipe data non-primitif. Metode ini memanggil konstruktor AbstractListimplementasi statis privat sederhana di Arrayskelas yang pada dasarnya menyimpan referensi array yang diberikan sebagai bidang dan mensimulasikan daftar dengan menimpa metode yang diperlukan.

Jika Anda dapat memilih antara tipe primtive atau tipe Wrapper untuk array Anda, saya akan menggunakan tipe Wrapper untuk situasi seperti itu tetapi tentu saja, itu tidak selalu berguna atau diperlukan. Hanya akan ada dua kemungkinan yang dapat Anda lakukan:

1) Anda dapat membuat kelas dengan metode statis untuk setiap array tipe data primitif ( boolean, byte, short, int, long, char, float, doublemengembalikan Iterable<WrapperType >. Metode ini akan menggunakan kelas anonim Iterator(selainIterable) yang diizinkan mengandung referensi argumen metode yang terdiri (misalnya, int[]) sebagai bidang untuk mengimplementasikan metode.

-> Pendekatan ini performan dan menghemat memori Anda (kecuali untuk memori metode yang baru dibuat, meskipun, menggunakan Arrays.asList()akan mengambil memori dengan cara yang sama)

2) Karena array tidak memiliki metode (seperti yang dibaca di samping Anda ditautkan) mereka tidak dapat memberikan Iteratorcontoh juga. Jika Anda benar-benar terlalu malas untuk menulis kelas baru, Anda harus menggunakan contoh dari kelas yang sudah ada yang mengimplementasikan Iterablekarena tidak ada jalan lain selain instantiating Iterableatau subtipe.
Cara HANYA untuk membuat implementasi turunan Koleksi yang adaIterableadalah menggunakan loop (kecuali Anda menggunakan kelas anonim seperti yang dijelaskan di atas) atau Anda membuat instance Iterablekelas pelaksana yang konstruktornya memungkinkan array tipe primtive (karena Object[]tidak memungkinkan array dengan elemen tipe primitif) tetapi sejauh yang saya tahu, Java API tidak menampilkan kelas seperti itu.

Alasan untuk perulangan dapat dijelaskan dengan mudah:
untuk setiap Koleksi yang Anda butuhkan Objek dan tipe data primtive bukan objek. Objek jauh lebih besar dari tipe primitif sehingga mereka membutuhkan data tambahan yang harus dihasilkan untuk setiap elemen dari array tipe primitif. Itu berarti jika dua cara tiga (menggunakan Arrays.asList(T...)atau menggunakan Koleksi yang ada) memerlukan agregat objek, Anda harus membuat untuk setiap nilai primitif dari Andaint[]Array objek pembungkus. Cara ketiga akan menggunakan array apa adanya dan menggunakannya dalam kelas anonim karena saya pikir itu lebih disukai karena kinerja yang cepat.

Ada juga strategi ketiga menggunakan Objectargumen as untuk metode di mana Anda ingin menggunakan array atau Iterabledan akan membutuhkan pemeriksaan tipe untuk mengetahui tipe argumen yang dimiliki, namun saya tidak akan merekomendasikannya sama sekali karena Anda biasanya perlu pertimbangkan bahwa Object tidak selalu tipe yang diperlukan dan Anda perlu kode terpisah untuk kasus-kasus tertentu.

Kesimpulannya, ini adalah kesalahan sistem Generic Type bermasalah dari Java yang tidak memungkinkan untuk menggunakan tipe primitif sebagai tipe generik yang akan menghemat banyak kode hanya dengan menggunakanArrays.asList(T...). Jadi Anda perlu memprogram untuk setiap tipe array primitif, Anda perlu, metode seperti itu (yang pada dasarnya tidak membuat perbedaan pada memori yang digunakan oleh program C ++ yang akan membuat untuk setiap argumen tipe yang digunakan sebagai metode terpisah.

CodingVirus01
sumber
3

Anda dapat menggunakan IterableOfdari Cactoos :

Iterable<String> names = new IterableOf<>(
  "Scott Fitzgerald", "Fyodor Dostoyevsky"
);

Kemudian, Anda dapat mengubahnya menjadi daftar menggunakan ListOf:

List<String> names = new ListOf<>(
  new IterableOf<>(
    "Scott Fitzgerald", "Fyodor Dostoyevsky"
  )
);

Atau sederhananya:

List<String> names = new ListOf<>(
  "Scott Fitzgerald", "Fyodor Dostoyevsky"
);
yegor256
sumber
1

Sementara jawaban yang serupa sudah semacam diposkan, saya pikir alasan untuk menggunakan PrimitiveIterator baru. Sebenarnya tidak jelas. Solusi yang baik adalah dengan menggunakan Java 8 PrimitiveIterator karena ini khusus untuk tipe int primitif (dan menghindari penalti tinju / unboxing tambahan):

    int[] arr = {1,2,3};
    // If you use Iterator<Integer> here as type then you can't get the actual benefit of being able to use nextInt() later
    PrimitiveIterator.OfInt iterator = Arrays.stream(arr).iterator();
    while (iterator.hasNext()) {
        System.out.println(iterator.nextInt());
        // Use nextInt() instead of next() here to avoid extra boxing penalty
    }

Ref: https://doc.bccnsoft.com/docs/jdk8u12-docs/api/java/util/PrimitiveIterator.OfInt.html

Binod Pant
sumber
-2

Dalam java8, aliran IntSteam dapat dikotakkan ke aliran Integer.

public static Iterable<Integer> toIterable(int[] ints) {
    return IntStream.of(ints).boxed().collect(Collectors.toList());
}

Saya pikir masalah kinerja berdasarkan ukuran array.

Soumyakanta Mohapatra
sumber