1. Latar belakang bagaimana iterator muncul

Anda sudah familiar dengan HashSet. Jika Anda benar-benar menyelidikinya, lebih dari sekedar membaca pelajaran, maka Anda seharusnya mengajukan pertanyaan ini:

Bagaimana cara menampilkan daftar semua elemen HashSet di layar? Lagi pula, antarmuka tidak memiliki get()dan set()metode!

Dan HashSettidak sendirian dalam keterbatasan ini. Selain HashSet, ada banyak koleksi lain yang tidak mengizinkan elemen diambil dengan indeks, karena elemen tidak memiliki urutan yang ditentukan.

Selama bertahun-tahun, pemrogram telah menemukan banyak struktur data yang kompleks, seperti grafik dan pohon. Atau daftar daftar.

Banyak wadah mengubah urutan elemennya saat elemen baru ditambahkan atau elemen yang sudah ada dihapus. Misalnya, daftar menyimpan elemen dalam urutan tertentu, dan saat elemen baru ditambahkan, elemen tersebut hampir selalu disisipkan di tengah daftar.

Dan kami juga mendapatkan situasi di mana ada wadah yang menyimpan elemen tetapi tidak dalam urutan tetap.

Sekarang katakanlah kita ingin menyalin semua elemen dari koleksi tersebut ke dalam array atau daftar. Kita perlu mendapatkan semua elemen. Kita tidak peduli dengan urutan iterasi pada elemen — hal yang penting adalah tidak melakukan iterasi pada elemen yang sama lebih dari sekali. Bagaimana kita melakukannya?


2. Iterator untuk koleksi

Iterator diusulkan sebagai solusi untuk masalah di atas.

Iterator adalah objek khusus yang terkait dengan koleksi, yang membantu melintasi semua elemen koleksi tanpa mengulanginya.

Anda dapat menggunakan kode berikut untuk mendapatkan iterator untuk koleksi apa pun:

Iterator<Type> it = name.iterator();

Di mana namenama variabel koleksi, Typejenis elemen koleksi, iterator()salah satu metode koleksi, dan itnama variabel iterator.

Objek iterator memiliki 3 metode:

metode Keterangan
Type next()
Mengembalikan elemen berikutnya dalam koleksi
boolean hasNext()
Memeriksa apakah ada elemen yang belum dilalui
void remove()
Menghapus elemen koleksi saat ini

Metode ini agak mirip dengan kelas nextInt)dan hasNextInt()metode Scanner.

Metode ini next()mengembalikan elemen koleksi berikutnya dari mana kita mendapatkan iteratornya.

Metode hasNext()memeriksa apakah koleksi memiliki elemen tambahan yang belum dikembalikan oleh iterator.

Berikut cara menampilkan semua elemen a HashSet:

Kode Catatan
HashSet<String> set = new HashSet<String>();

set.add("Hallo");
set.add("Hello");
set.add("Hola");
set.add("Bonjour");
set.add("Ciao");
set.add("Namaste");

Iterator<String> it = set.iterator();
while (it.hasNext())
{
   String str = it.next();
   System.out.println(str);
}
Buat HashSetobjek yang menyimpan Stringelemen.


Kami menambahkan salam dalam berbagai bahasa ke setvariabel.




Dapatkan objek iterator untuk setset.
Selama masih ada elemen

Dapatkan elemen selanjutnya
Tampilkan elemen di layar


3. For-eachputaran

Kerugian utama dari iterator adalah kode Anda menjadi lebih rumit daripada menggunakan forloop.

Untuk membandingkan, mari tampilkan daftar menggunakan forloop dan juga menggunakan iterator:

Iterator untuk putaran
ArrayList<String> list = new ArrayList<String>();

Iterator<String> it = list.iterator();
while (it.hasNext())
{
   String str = it.next();
   System.out.println(str);
}
ArrayList<String> list = new ArrayList<String>();

for (int i = 0; i < list.size(); i++)
{
   String str = list.get(i);
   System.out.println(str);
}

Ya, jauh lebih baik untuk melintasi elemen menggunakan ArrayListloop — semuanya ternyata lebih pendek.

Tapi pencipta Java kembali memutuskan untuk menuangkan gula pada kami. Beruntung bagi kami, itu adalah gula sintaksis .

Mereka memberi Java jenis loop baru dan menyebutnya loop for-each. Begini tampilannya secara umum:

for(Type name:collection)

Di mana collectionnama variabel koleksi, Typejenis elemen dalam koleksi, dan namenama variabel yang mengambil nilai berikutnya dari koleksi pada setiap iterasi loop.

Jenis loop ini mengiterasi semua elemen koleksi menggunakan iterator implisit. Inilah cara kerjanya:

Untuk-setiap loop Apa yang dilihat oleh kompiler: Ulangi dengan iterator
ArrayList<String> list = new ArrayList<String>();

for (String str: list)
{
   System.out.println(str);
}
ArrayList<String> list = new ArrayList<String>();
Iterator<String> it = list.iterator();

while (it.hasNext())
{
   String str = it.next();
   System.out.println(str);
}

Ketika kompiler menemukan for-eachloop dalam kode Anda, itu hanya menggantinya dengan kode di sebelah kanan: itu menambahkan panggilan untuk mendapatkan iterator bersama dengan panggilan metode lain yang hilang.

Pemrogram menyukai for-eachloop dan hampir selalu menggunakannya saat mereka perlu mengulangi semua elemen koleksi.

Bahkan mengulangi ArrayListdaftar menggunakan for-eachloop terlihat lebih pendek:

Untuk-setiap loop untuk putaran
ArrayList<String> list = new ArrayList<String>();

for (String str: list)
{
   System.out.println(str);
}
ArrayList<String> list = new ArrayList<String>();

for (int i = 0; i < list.size(); i++)
{
   String str = list.get(i);
   System.out.println(str);
}


4. Menghapus elemen dalam satu for-eachlingkaran

Loop for-eachmemiliki satu kelemahan: tidak dapat menghapus elemen dengan benar. Jika Anda menulis kode seperti ini, Anda akan mendapatkan kesalahan.

Kode Catatan
ArrayList<String> list = new ArrayList<String>();

list.add("Hallo");
list.add("Hello");
list.add("Hola");
list.add("Bonjour");
list.add("Ciao");
list.add("Namaste");

for (String str: list)
{
   if (str.equals("Hello"))
      list.remove(str);
}












Operasi hapus akan menghasilkan kesalahan!

Ini adalah kode yang sangat bagus dan mudah dipahami, tetapi tidak akan berhasil.

Penting!

Anda tidak dapat mengubah koleksi saat Anda melintasinya dengan iterator.

Ada tiga cara untuk mengatasi batasan ini.

1. Gunakan jenis loop yang berbeda

When traversing an ArrayList collection, Anda dapat menggunakan loop biasa dengan ivariabel penghitung.

Kode
for (int i = 0; i < list.size(); i++)
{
   String str = list.get(i);

   if (str.equals("Hello"))
   {
      list.remove(str);
      i--; // We need to decrease i, because the remove operation shifted the elements
   }
}

Namun, opsi ini tidak cocok untuk HashSetdan HashMapkoleksi

2. Gunakan iterator eksplisit

Anda dapat menggunakan iterator secara eksplisit dan memanggil metodenya remove().

Versi yang berfungsi Versi yang tidak berfungsi
Iterator<String> it = set.iterator();
while (it.hasNext())
{
   String str = it.next();
   if (str.equals("Hello"))
       it.remove();
}
for (String str: list)
{
   if (str.equals("Hello"))
      list.remove(str);
}

Perhatikan bahwa kami memanggil remove()metode pada objek iterator! Iterator menyadari bahwa item tersebut telah dihapus dan dapat menangani situasi dengan benar.

3. Gunakan salinan koleksi

Anda juga dapat membuat salinan koleksi lalu menggunakan salinan tersebut dalam satu for-eachlingkaran dan menghapus elemen dari koleksi asli.

Kode Catatan
ArrayList<String> listCopy = new ArrayList(list);

for (String str: listCopy)
{
   if (str.equals("Hello"))
      list.remove(str);
}
Membuat salinan koleksi sangat mudah



Loop menggunakan iterator untuk salinan koleksi.
Elemen dihapus dari listkoleksi.

Koleksinya disalin agak cepat, karena elemennya sendiri tidak diduplikasi. Sebagai gantinya, koleksi baru menyimpan referensi ke elemen yang sudah ada di koleksi lama.