Kotlin menyediakan banyak fitur bahasa yang membuat implementasi algoritma menjadi lebih ringkas, ekspresif, dan aman secara tipe dibanding Java tradisional. Pada chapter ini kita akan memadukan fitur-fitur tersebut—extension function, sealed class, sequence, scope function, dan tail recursion—dalam satu berkas yang dapat dijalankan. Pembaca akan melihat bagaimana algoritma Kadane untuk maximum subarray menjadi method pada IntArray, bagaimana sealed class HasilPencarian memodelkan hasil pencarian dengan aman secara tipe, dan bagaimana asSequence() menghemat memori pada pipeline besar. Chapter ini menutup section Algorithms dan Data Structures dengan ringkasan praktik terbaik Kotlin.
Extension Function untuk Algoritma #
Extension function memungkinkan kita menambahkan method ke class tanpa mewarisinya. Algoritma yang beroperasi pada koleksi tertentu dapat diorganisir sebagai method yang terasa natural saat dipanggil.
fun IntArray.maxSubarraySum(): Int {
var maxSoFar = Int.MIN_VALUE
var maxEndingHere = 0
for (x in this) {
maxEndingHere += x
if (maxSoFar < maxEndingHere) maxSoFar = maxEndingHere
if (maxEndingHere < 0) maxEndingHere = 0
}
return maxSoFar
}
// Pemanggilan: arr.maxSubarraySum()Algoritma Kadane di atas kini menjadi method pada IntArray sehingga pemanggilan terasa seperti API bawaan Kotlin.
Sealed Class untuk State Algoritma #
Sealed class membatasi subkelas hanya pada yang dideklarasikan di dalam file yang sama. Kombinasi dengan when ekspresif memungkinkan compiler memverifikasi bahwa semua cabang tertangani.
sealed class HasilPencarian<out T> {
data class Ditemukan<T>(val index: Int, val value: T) : HasilPencarian<T>()
data class TidakDitemukan(val jumlahDiperiksa: Int) : HasilPencarian<Nothing>()
}Hasil pencarian kini berbentuk nilai Ditemukan atau TidakDitemukan—tidak ada kemungkinan null yang harus dihandle secara terpisah.
Sequence untuk Lazy Evaluation #
Sequence mengevaluasi elemen secara lazy (saat dibutuhkan) dan merangkai operasi tanpa membuat koleksi antara. Hal ini menghemat memori pada pipeline yang besar.
(2..1_000_000).asSequence()
.filter { isPrime(it) }
.map { it * it }
.take(10)
.toList()Tanpa asSequence(), setiap filter dan map membuat List baru—memboros memori untuk jutaan elemen.
Implementasi Lengkap #
// Demo Implementasi Algoritma dengan Kotlin idiomatic.
// === Extension function: algoritma sebagai method ===
fun IntArray.sumInRange(low: Int, high: Int): Int {
var sum = 0
for (i in low..high) {
if (i < size) sum += this[i]
}
return sum
}
fun IntArray.maxSubarraySum(): Int {
var maxSoFar = Int.MIN_VALUE
var maxEndingHere = 0
for (x in this) {
maxEndingHere += x
if (maxSoFar < maxEndingHere) maxSoFar = maxEndingHere
if (maxEndingHere < 0) maxEndingHere = 0
}
return maxSoFar
}
// === Sealed class untuk state algoritma ===
sealed class HasilPencarian<out T> {
data class Ditemukan<T>(val index: Int, val value: T) : HasilPencarian<T>()
data class TidakDitemukan(val jumlahDiperiksa: Int) : HasilPencarian<Nothing>()
}
fun <T : Comparable<T>> linearSearchSealed(arr: List<T>, target: T): HasilPencarian<T> {
var diperiksa = 0
for ((i, x) in arr.withIndex()) {
diperiksa++
if (x == target) return HasilPencarian.Ditemukan(i, x)
}
return HasilPencarian.TidakDitemukan(diperiksa)
}
// === Sequence untuk lazy evaluation ===
fun isPrime(n: Int): Boolean {
if (n < 2) return false
if (n == 2) return true
if (n % 2 == 0) return false
var i = 3
while (i.toLong() * i <= n) {
if (n % i == 0) return false
i += 2
}
return true
}
fun primesUpTo(n: Int): List<Int> {
return (2..n).asSequence().filter { isPrime(it) }.toList()
}
fun sumOfPrimesUpTo(n: Int): Long {
return (2..n).asSequence().filter { isPrime(it) }.sumOf { it.toLong() }
}
// === Algoritma fungsi: pipeline dengan sequence ===
fun pipelineFungsional(data: List<Int>): List<Int> {
return data.asSequence()
.filter { it > 0 } // hanya positif
.map { it * it } // kuadratkan
.distinct() // hapus duplikat
.sorted() // urutkan
.toList()
}
// === Scope function untuk algoritma ===
data class Statistik(val mean: Double, val median: Double, val modus: Int?)
fun hitungStatistik(data: List<Int>): Statistik = data.run {
val sorted = sorted()
val mean = average()
val median = if (size % 2 == 0) {
(sorted[size / 2 - 1] + sorted[size / 2]) / 2.0
} else {
sorted[size / 2].toDouble()
}
val modus = groupingBy { it }.eachCount().maxByOrNull { it.value }?.key
Statistik(mean, median, modus)
}
// === Tail recursion ===
tailrec fun factorialTail(n: Int, acc: Long = 1): Long {
if (n <= 1) return acc
return factorialTail(n - 1, acc * n)
}
tailrec fun gcd(a: Int, b: Int): Int {
if (b == 0) return a
return gcd(b, a % b)
}
fun main() {
println("=== Demo Implementasi Algoritma dengan Kotlin ===")
println("--- Extension function ---")
val arr = intArrayOf(3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5)
println("Array: ${arr.toList()}")
println("Sum range [2..5]: ${arr.sumInRange(2, 5)}")
val withNeg = intArrayOf(-2, 1, -3, 4, -1, 2, 1, -5, 4)
println("Array (dengan negatif): ${withNeg.toList()}")
println("Max subarray sum (Kadane): ${withNeg.maxSubarraySum()}")
println()
println("--- Sealed class untuk state pencarian ---")
val data = listOf(5, 3, 8, 1, 9, 2, 7)
listOf(9, 100, 5).forEach { target ->
val hasil = linearSearchSealed(data, target)
val pesan = when (hasil) {
is HasilPencarian.Ditemukan -> "ditemukan di index ${hasil.index} (value=${hasil.value})"
is HasilPencarian.TidakDitemukan -> "tidak ada setelah ${hasil.jumlahDiperiksa} pemeriksaan"
}
println("Cari $target: $pesan")
}
println()
println("--- Sequence untuk lazy evaluation ---")
println("Prima sampai 50: ${primesUpTo(50)}")
println("Jumlah prima sampai 100: ${sumOfPrimesUpTo(100)}")
println()
println("--- Pipeline fungsional ---")
val input = listOf(1, -2, 3, 3, -4, 5, 5, 6, 0, 7)
println("Input: $input")
println("Pipeline (filter>0, kuadrat, distinct, sorted): ${pipelineFungsional(input)}")
println()
println("--- Scope function untuk statistik ---")
val statistik = hitungStatistik(listOf(85, 90, 78, 92, 88, 76, 95, 88, 80, 88))
println("Data: ${listOf(85, 90, 78, 92, 88, 76, 95, 88, 80, 88)}")
println("Statistik: mean=${"%.2f".format(statistik.mean)}, median=${statistik.median}, modus=${statistik.modus}")
println()
println("--- Tail recursion ---")
println("factorial(20) = ${factorialTail(20)}")
println("gcd(48, 36) = ${gcd(48, 36)}")
println("gcd(17, 5) = ${gcd(17, 5)}")
println()
println("--- Idiomatic Kotlin: compareBy dengan chained comparator ---")
data class Produk(val nama: String, val kategori: String, val harga: Int)
val produk = listOf(
Produk("Buku", "ATK", 15000),
Produk("Pulpen", "ATK", 5000),
Produk("Baju", "Fashion", 75000),
Produk("Celana", "Fashion", 75000),
Produk("Penggaris", "ATK", 5000)
)
println("Sorted by kategori then harga then nama:")
produk.sortedWith(compareBy({ it.kategori }, { it.harga }, { it.nama })).forEach {
println(" ${it.kategori} | Rp${it.harga} | ${it.nama}")
}
}Output yang Dihasilkan:
=== Demo Implementasi Algoritma dengan Kotlin ===
--- Extension function ---
Array: [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]
Sum range [2..5]: 19
Array (dengan negatif): [-2, 1, -3, 4, -1, 2, 1, -5, 4]
Max subarray sum (Kadane): 6
--- Sealed class untuk state pencarian ---
Cari 9: ditemukan di index 4 (value=9)
Cari 100: tidak ada setelah 7 pemeriksaan
Cari 5: ditemukan di index 0 (value=5)
--- Sequence untuk lazy evaluation ---
Prima sampai 50: [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]
Jumlah prima sampai 100: 1060
--- Pipeline fungsional ---
Input: [1, -2, 3, 3, -4, 5, 5, 6, 0, 7]
Pipeline (filter>0, kuadrat, distinct, sorted): [1, 9, 25, 36, 49]
--- Scope function untuk statistik ---
Data: [85, 90, 78, 92, 88, 76, 95, 88, 80, 88]
Statistik: mean=86.00, median=88.0, modus=88
--- Tail recursion ---
factorial(20) = 2432902008176640000
gcd(48, 36) = 12
gcd(17, 5) = 1
--- Idiomatic Kotlin: compareBy dengan chained comparator ---
Sorted by kategori then harga then nama:
ATK | Rp5000 | Penggaris
ATK | Rp5000 | Pulpen
ATK | Rp15000 | Buku
Fashion | Rp75000 | Baju
Fashion | Rp75000 | CelanaBedah Kode Baris-per-Baris #
Mari kita amati bagian kunci:
IntArray.sumInRange(baris 4-10): Extension function menerimalowdanhighsebagai parameter. Di dalam function,thismerujuk keIntArraypenerima. Pemanggilanarr.sumInRange(2, 5)terasa seperti method bawaan Kotlin—API jadi terasa alami.IntArray.maxSubarraySum(baris 12-21): Algoritma Kadane melacakmaxSoFar(subarray maksimum global) danmaxEndingHere(subarray maksimum berakhir di indeks saat ini). BilamaxEndingHerejatuh di bawah nol, reset ke nol karena subarray negatif tidak akan meningkatkan jumlah di iterasi berikutnya.HasilPencarian(baris 24-36): Sealed class generik dengan dua subkelas.Ditemukan<T>membawa indeks dan nilai;TidakDitemukanmembawa jumlah elemen yang diperiksa. TipeNothingpadaTidakDitemukanmembuatnya cocok untuk semuaHasilPencarian<T>berkat deklarasiout T(covariance). Konstruksiwhenpada baris 111-114 mendapat keuntungan dari smart cast—hasil.indextersedia tanpa cast eksplisit.primesUpTo(baris 51-53): MemakaiasSequence()untuk membuat pipeline lazy. Methodfiltertidak membuat list antara; setiap elemen mengalir melalui filter dan langsung masuk ketoList()di akhir.pipelineFungsional(baris 60-67): Pipeline lima tahap—filter positif, kuadratkan, hapus duplikat, urutkan, kembalikan list. Berkat sequence, setiap tahap diproses secara streaming tanpa koleksi antara. Untuk input[1, -2, 3, 3, -4, 5, 5, 6, 0, 7], hasilnya[1, 9, 25, 36, 49]—kuadrat dari nilai positif yang unik dan terurut.hitungStatistik(baris 72-82): Memakaidata.run { ... }sehinggathisdi dalam block adalahList<Int>. Methodsorted(),average(),groupingBy { }dapat dipanggil langsung tanpa prefixdata.. Hasilnya data classStatistikyang merangkum tiga nilai statistik.factorialTaildangcd(baris 85-93): Modifiertailrecmenginstruksikan compiler mengubah recursion menjadi loop iteratif bila recursive call adalah operasi terakhir.factorialTail(20)mengembalikan2432902008176640000tanpa risiko stack overflow.
Praktik Terbaik Ringkas #
Implementasi algoritma idiomatic Kotlin menggabungkan beberapa prinsip: extension function untuk API yang natural, sealed class untuk state yang aman secara tipe, sequence untuk pipeline hemat memori, scope function untuk reduksi boilerplate, dan tail recursion untuk rekursi yang aman. Kombinasi ini menghasilkan kode yang ringkas, ekspresif, dan setara dalam kinerja dengan implementasi imperatif tradisional.
Penutup #
Section Algorithms dan Data Structures telah menelusuri perjalanan lengkap—dari kompleksitas waktu, struktur data dasar, algoritma searching/sorting, recursion/backtracking, greedy, dynamic programming, graph algorithms, hingga penulisan idiomatic Kotlin. Pemahaman ini menjadi bekal untuk menyelesaikan masalah nyata di produksi dengan kode yang efisien dan mudah dibaca.
Referensi #
Referensi Lanjutan #
- Kotlin Collections overview — pengantar koleksi Kotlin sebagai dasar implementasi algoritma yang idiomatik
- Kotlin Sequences — dokumentasi Sequences untuk memproses data besar secara lazy pada algoritma pipeline