Iterasi adalah operasi yang dilakukan pada hampir setiap collection. Kotlin menyediakan banyak gaya iterasi: for-in tradisional, forEach fungsional, forEachIndexed dengan indeks, hingga iterator manual. Pada chapter ini kita akan membandingkan tiap gaya dan memahami protokol Iterator<T> yang menjadi dasar semuanya. Kita juga akan membuat iterator kustom yang menghasilkan deret Fibonacci sebagai contoh penerapan protokol tersebut.
Gaya Iterasi Umum #
| Bentuk | Penggunaan | Cocok untuk |
|---|---|---|
for (x in coll) | Loop tradisional | Iterasi sederhana, bisa pakai break/continue |
coll.forEach { } | Lambda | Pipeline fungsional, tidak ada break |
coll.forEachIndexed { i, x -> } | Lambda dengan indeks | Perlu tahu posisi elemen |
for (i in coll.indices) | Indeks | Akses coll[i] eksplisit |
for ((i, x) in coll.withIndex()) | IndexedValue | Indeks + nilai, ringkas |
for ((k, v) in map) | Destructuring | Iterasi Map.Entry |
For-in dan ForEach #
for (x in coll) adalah sintaks loop tradisional. Di balik layar, kompilator mengubahnya menjadi coll.iterator() lalu while (iter.hasNext()) { val x = iter.next() }. Loop for-in mendukung break dan continue.
forEach { } adalah function higher-order yang menerima lambda (T) -> Unit. Lambda dipanggil sekali per elemen. Karena forEach adalah function biasa (bukan loop), break dan continue tidak bisa dipakai — kecuali dengan labeled return dari dalam lambda.
withIndex dan indices #
Untuk mendapatkan indeks bersamaan dengan nilai, ada dua pilihan:
for (i in coll.indices)lalu aksescoll[i]— iterasi indeks.for ((i, x) in coll.withIndex())— deklarasi destructuring dariIndexedValue.
withIndex lebih ringkas dan menghindari pemanggilan coll[i] berulang yang bisa berbiaya pada struktur akses lambat.
Iterator Protocol #
Setiap Iterable<T> memiliki metode iterator() yang mengembalikan Iterator<T>. Iterator<T> adalah interface dengan dua metode:
interface Iterator<out T> {
operator fun hasNext(): Boolean
operator fun next(): T
}Kita bisa membuat iterator kustom untuk menghasilkan deret nilai secara lazy. Contoh klasik adalah deret Fibonacci yang dihitung on-demand:
class FibIterator : Iterator<Int> {
var a = 0
var b = 1
override fun hasNext() = a < 100
override fun next(): Int {
val cur = a
val next = a + b
a = b; b = next
return cur
}
}Iterator ini tidak menyimpan seluruh deret Fibonacci di memori — setiap elemen dihitung saat next() dipanggil. Inilah inti evaluasi lazy yang akan kita jumpai lagi pada chapter Sequences.
Contoh Lengkap #
fun main() {
val buah = listOf("Apel", "Jeruk", "Pisang", "Mangga")
// for loop dengan in
print("For-each: ")
for (b in buah) {
print("$b ")
}
println()
// forEach dengan lambda
print("forEach: ")
buah.forEach { print("$it ") }
println()
// forEachIndexed
println("forEachIndexed:")
buah.forEachIndexed { i, b -> println(" [$i] $b") }
// for dengan indeks: indices
println("Dengan indices:")
for (i in buah.indices) {
println(" $i -> ${buah[i]}")
}
// withIndex: indeks + nilai
println("withIndex:")
for ((i, b) in buah.withIndex()) {
println(" $i: $b")
}
// Iterasi Map
val peta = mapOf("Jepang" to "Tokyo", "Korea" to "Seoul")
println("Iterasi Map entries:")
for ((k, v) in peta) {
println(" $k -> $v")
}
// Iterasi String (CharSequence)
val teks = "Kotlin"
print("Iterasi String: ")
for (ch in teks) {
print("$ch ")
}
println()
// Iterator manual: hasNekt / next
println("Iterator manual:")
val iter = buah.iterator()
while (iter.hasNext()) {
println(" ${iter.next()}")
}
// Iterator kustom: deret Fibonacci terbatas
class FibIterator : Iterator<Int> {
var a = 0; var b = 1
override fun hasNext() = a < 100
override fun next(): Int {
val cur = a
val next = a + b
a = b; b = next
return cur
}
}
val fibSeq = object : Iterable<Int> {
override fun iterator() = FibIterator()
}
print("Fibonacci < 100: ")
for (n in fibSeq) print("$n ")
println()
// ListIterator: bisa maju dan mundur
val li = buah.listIterator()
println("Maju:")
while (li.hasNext()) println(" ${li.nextIndex()}: ${li.next()}")
println("Mundur:")
while (li.hasPrevious()) println(" ${li.previousIndex()}: ${li.previous()}")
// Iterasi range dengan step
print("Range step 3: ")
for (i in 0..10 step 3) print("$i ")
println()
}Output yang Dihasilkan:
For-each: Apel Jeruk Pisang Mangga
forEach: Apel Jeruk Pisang Mangga
forEachIndexed:
[0] Apel
[1] Jeruk
[2] Pisang
[3] Mangga
Dengan indices:
0 -> Apel
1 -> Jeruk
2 -> Pisang
3 -> Mangga
withIndex:
0: Apel
1: Jeruk
2: Pisang
3: Mangga
Iterasi Map entries:
Jepang -> Tokyo
Korea -> Seoul
Iterasi String: K o t l i n
Iterator manual:
Apel
Jeruk
Pisang
Mangga
Fibonacci < 100: 0 1 1 2 3 5 8 13 21 34 55 89
Maju:
0: Apel
1: Jeruk
2: Pisang
3: Mangga
Mundur:
3: Mangga
2: Pisang
1: Jeruk
0: Apel
Range step 3: 0 3 6 9Mari kita bedah baris-baris penting di atas:
for (b in buah): kompilator mengubah loop ini menjadival iter = buah.iterator(); while (iter.hasNext()) { val b = iter.next(); ... }. Inilah mengapafor-inbekerja pada setiapIterable.buah.forEach { print("$it ") }: lambda dipanggil sekali per elemen.itadalah nama implisit untuk parameter tunggal. KarenaforEachadalah function, kita tidak bisa menggunakanbreak— kecuali denganreturn@forEachberlabel.buah.indices: mengembalikanIntRangedari0hinggasize - 1. Iterasifor (i in buah.indices)memberi kita setiap indeks untuk aksesbuah[i].for ((i, b) in buah.withIndex()):withIndex()mengembalikanIterable<IndexedValue<T>>.IndexedValuememilikiindexdanvalue. Destructuring mengeluarkan keduanya menjadiidanb.for ((k, v) in peta): iterasi padaMapmenghasilkanMap.Entry. Destructuring mengeluarkankeydanvalue. Implementasi defaultLinkedHashMapmempertahankan urutan penyisipan.for (ch in teks):StringmengimplementasikanCharSequenceyangIterable<Char>. Tiap iterasi menghasilkan satuChar.val iter = buah.iterator(); while (iter.hasNext()) { iter.next() }: ini adalah bentuk manual yang setara denganfor (b in buah). Jarang ditulis tangan kecuali untuk algoritma khusus yang memerlukan kontrol iterator eksplisit.FibIterator:hasNext()mengembalikantrueselamaa < 100.next()mengembalikan nilai Fibonacci saat ini dan menggeser state. Tidak ada batas atas tetap — iterator menghitung nilai berikutnya saat diminta.object : Iterable<Int> { override fun iterator() = FibIterator() }: anonymous object yang mengimplementasiIterable. Setiap pemanggilaniterator()membuat instance baru agar iterasi bisa diulang.li.hasPrevious()/li.previous():ListIteratoradalah iterator dua arah yang bisa maju dan mundur.nextIndex()danpreviousIndex()memberi indeks tanpa menggerakkan kursor.
Referensi Lanjutan #
- Collection operations
— panduan
for,forEach,forEachIndexedpada collection - Collection elements — akses elemen selama iterasi
Setelah memahami iterasi eager, kita akan melihat evaluasi lazy melalui Sequence yang menghindari alokasi list antara pada pipeline operasi.