Map read-only statis setelah dibuat. Begitu kita perlu memperbarui nilai, menambah key baru, atau menghapus pasangan saat program berjalan, MutableMap<K, V> menjadi pilihan. Pada chapter ini kita akan mempraktikkan operasi mutasi utama, pola idiomatik seperti getOrPut dan compute, serta alternatif yang aman untuk akses paralel.
Membuat MutableMap #
MutableMap dibuat dengan mutableMapOf(...). Implementasi default adalah LinkedHashMap, sehingga urutan penyisipan key tetap dipertahankan.
val stok: MutableMap<String, Int> = mutableMapOf("apel" to 5, "jeruk" to 3)
val kosong: MutableMap<String, Int> = mutableMapOf()
val hashed: MutableMap<String, Int> = hashMapOf()Operasi Mutasi Utama #
| Metode | efek | Catatan |
|---|---|---|
map[k] = v / put(k, v) | menambah atau mengganti | Mengembalikan nilai lama atau null |
putIfAbsent(k, v) | menambah hanya jika k belum ada | Mengembalikan nilai yang sudah ada atau null |
getOrPut(k) { default } | mengambil atau memasukkan default | Idiom cache ringkas |
compute(k) { _, v -> ... } | menghitung ulang nilai berdasarkan nilai lama | Lambda boleh mengembalikan null untuk menghapus entry |
remove(k) | menghapus entry dengan key k | Mengembalikan nilai lama atau null |
remove(k, v) | menghapus hanya jika nilai cocok | Mengembalikan Boolean |
clear() | mengosongkan map | size menjadi 0 |
getOrPut: Pola Cache Idiomatik #
Pola umum dalam kode Kotlin adalah mengambil nilai dari map, atau jika belum ada, menghitung lalu menyimpannya. Tanpa getOrPut, kita menulis tiga langkah: cek containsKey, ambil, atau masukkan. Dengan getOrPut, satu baris cukup.
val cache = mutableMapOf<String, Int>()
val panjang = cache.getOrPut("kotlin") { "kotlin".length }Lambda { "kotlin".length } hanya dievaluasi ketika key "kotlin" tidak ada dalam map.
compute: Mutasi Berdasarkan Nilai Lama #
compute menerima key dan lambda yang menerima key serta nilai lama (bisa null). Lambda mengembalikan nilai baru — jika null, entry dihapus dari map.
stok.compute("apel") { _, v -> v?.let { it * 2 } } // kalikan dengan 2
stok.compute("apel") { _, _ -> 50 } // tuk paksa
stok.compute("jeruk") { _, _ -> null } // hapus entryContoh Lengkap #
fun main() {
val stok: MutableMap<String, Int> = mutableMapOf(
"apel" to 5,
"jeruk" to 3,
"pisang" to 8
)
println("Awal: $stok")
// put: menambah atau mengganti
stok["mangga"] = 4
println("Setelah put mangga: $stok")
stok["apel"] = 10 // mengganti nilai lama
println("Setelah put apel (ganti): $stok")
stok.putIfAbsent("apel", 99) // sudah ada -> tidak diubah
println("Setelah putIfAbsent apel: $stok")
stok.putIfAbsent("anggur", 7)
println("Setelah putIfAbsent anggur: $stok")
// getOrPut
val jumlahNanas = stok.getOrPut("nanas") { 0 }
println("getOrPut nanas: $jumlahNanas, stok=$stok")
val jumlahApel = stok.getOrPut("apel") { 100 }
println("getOrPut apel (sudah ada): $jumlahApel")
// compute: menghitung ulang nilai
stok.compute("apel") { _, v -> v?.let { it * 2 } }
println("Setelah compute apel (kali 2): $stok")
stok.compute("apel") { _, _ -> 50 }
println("Setelah compute apel (set 50): $stok")
// remove
stok.remove("jeruk")
println("Setelah remove jeruk: $stok")
stok.remove("pisang", 99) // nilai tidak cocok -> tidak dihapus
println("Setelah remove pisang (nilai tidak cocok): $stok")
// Iterasi mutable + filter
println("Stok > 5:")
stok.filter { (_, j) -> j > 5 }.forEach { (k, v) -> println(" $k: $v") }
// toMutableMap (salinan)
val salinan = stok.toMutableMap()
salinan.clear()
println("Salinan setelah clear: $salinan")
println("Asli tetap: $stok")
// Catatan thread safety
println("MutableMap TIDAK aman untuk akses multi-thread tanpa sinkronisasi")
}Output yang Dihasilkan:
Awal: {apel=5, jeruk=3, pisang=8}
Setelah put mangga: {apel=5, jeruk=3, pisang=8, mangga=4}
Setelah put apel (ganti): {apel=10, jeruk=3, pisang=8, mangga=4}
Setelah putIfAbsent apel: {apel=10, jeruk=3, pisang=8, mangga=4}
Setelah putIfAbsent anggur: {apel=10, jeruk=3, pisang=8, mangga=4, anggur=7}
getOrPut nanas: 0, stok={apel=10, jeruk=3, pisang=8, mangga=4, anggur=7, nanas=0}
getOrPut apel (sudah ada): 10
Setelah compute apel (kali 2): {apel=20, jeruk=3, pisang=8, mangga=4, anggur=7, nanas=0}
Setelah compute apel (set 50): {apel=50, jeruk=3, pisang=8, mangga=4, anggur=7, nanas=0}
Setelah remove jeruk: {apel=50, pisang=8, mangga=4, anggur=7, nanas=0}
Setelah remove pisang (nilai tidak cocok): {apel=50, pisang=8, mangga=4, anggur=7, nanas=0}
Stok > 5:
apel: 50
pisang: 8
anggur: 7
Salinan setelah clear: {}
Asli tetap: {apel=50, pisang=8, mangga=4, anggur=7, nanas=0}
MutableMap TIDAK aman untuk akses multi-thread tanpa sinkronisasiMari kita bedah baris-baris penting di atas:
stok["mangga"] = 4: subscript di sisi penugasan setara denganput("mangga", 4). Karena key baru, entry ditambahkan.stok.putIfAbsent("apel", 99): key"apel"sudah ada (nilai10), sehingga pemanggilan tidak mengubah apa pun dan mengembalikan nilai yang sudah ada.stok.getOrPut("nanas") { 0 }: key"nanas"tidak ada, lambda dievaluasi dan hasilnya (0) dimasukkan serta dikembalikan.stok.compute("apel") { _, v -> v?.let { it * 2 } }: lambda menerimavlama (10) dan mengembalikan20. Bilavnull(key tidak ada),v?.letmengembalikannulldan entry akan dihapus.stok.remove("pisang", 99): varian dua argumen hanya menghapus jika nilai yang tersimpan sama persis dengan argumen kedua. Karena nilai"pisang"adalah8(bukan99), entry tetap utuh dan metode mengembalikanfalse.
Alternatif Aman untuk Konkurensi #
MutableMap default tidak aman untuk akses paralel. Untuk skenario multi-thread, pilih alternatif berikut:
| Implementasi | Sumber | Karakteristik |
|---|---|---|
ConcurrentHashMap | java.util.concurrent | Aman baca-tulis paralel, iterasi weakly-consistent |
Collections.synchronizedMap | java.util.Collections | Membungkus map dengan sinkronisasi method-level |
ConcurrentSkipListMap | java.util.concurrent | Aman paralel + urutan natural / komparator |
import java.util.concurrent.ConcurrentHashMap
val cacheParalel: MutableMap<String, Int> = ConcurrentHashMap()
cacheParalel["a"] = 1
cacheParalel.compute("a") { _, v -> v?.plus(1) }Referensi Lanjutan #
- Map
— panduan resmi
MutableMap<K, V>danHashMap - Map API reference
— API reference
kotlin.collections.Map - Collection write operations
— panduan
put,remove,clearpadaMutableMap
Setelah memahami Map dan MutableMap, kita akan beralih ke struktur mirip list tetapi dengan ukuran tetap: Array.