Map<K, V> menyimpan pasangan key-value. Berbeda dengan List dan Set yang menyimpan nilai tunggal, Map memetakan sebuah key ke sebuah value — misalnya kode negara ke kota, atau ID pengguna ke profil. Pada chapter ini kita akan membuat map, mengakses nilai berdasarkan key, mengiterasi pasangan, dan memahami sifat urutan penyisipan yang menjadi jaminan default di pustaka standar Kotlin.

Membuat Map Read-Only #

Map dibuat menggunakan mapOf(...) dengan pasangan key to value. Infix function to membuat objek Pair<K, V> yang kemudian dimasukkan ke map.

kotlin Contoh: deklarasi Map
val kota: Map<String, String> = mapOf(
    "Jepang" to "Tokyo",
    "Korea" to "Seoul",
    "Indonesia" to "Jakarta"
)
val kosong: Map<String, Int> = emptyMap()
val dariList = listOf("a" to 1, "b" to 2).toMap()

Map read-only tidak bisa diubah isinya; tidak ada metode put atau remove. Nilai yang disimpan bisa nullable (Map<String, Int?>), dan value dengan key yang sama hanya disimpan sekali — key berikutnya menimpa key sebelumnya pada saat konstruksi.

Mengakses Nilai #

Nilai diakses melalui operator subscript map[key] atau metode get(key). Karena key mungkin tidak ada, hasil aksesnya bertipe nullable (V?) meskipun value sebenarnya non-null.

OperasiMengembalikanCatatan
map[key]V?null jika key tidak ada
map.get(key)V?Sama dengan subscript
map.getOrDefault(key, default)VMengembalikan default jika key tidak ada
map.getOrElse(key) { ... }VLambda dievaluasi saat key tidak ada
map.getValue(key)VMelempar NoSuchElementException jika tidak ada

keys, values, entries #

Tiga properti ini memberi kita view ke isi map:

  • map.keys — Set<K> berisi semua key.
  • map.values — Collection<V> berisi semua value (boleh duplikat).
  • map.entries — Set<Map.Entry<K, V>> berisi pasangan key-value.

Ketiganya adalah view — perubahan pada map (versi mutable) akan tercermin pada ketiga collection ini.

Iterasi dan Urutan Penyisipan #

mapOf di JVM menghasilkan LinkedHashMap, sehingga iterasi mempertahankan urutan penyisipan. Dua gaya iterasi yang paling sering dipakai:

kotlin Contoh: iterasi Map
for ((key, value) in map) {
    println("$key -> $value")
}

map.entries.forEach { (k, v) -> println("$k: $v") }

Deklarasi (k, v) adalah destructuring declaration — Kotlin memecah Map.Entry menjadi dua variabel.

Contoh Lengkap #

Berikut program yang merangkum operasi dasar Map:

kotlin
fun main() {
    val kota: Map<String, String> = mapOf(
        "Jepang" to "Tokyo",
        "Korea" to "Seoul",
        "Indonesia" to "Jakarta"
    )

    // Akses nilai dengan operator []
    println("Kota Jepang: ${kota["Jepang"]}")
    println("Kota Prancis: ${kota["Prancis"]}")

    // Akses aman
    println("getOrElse: ${kota.getOrElse("Prancis") { "(tidak ada)" }}")
    println("getOrDefault: ${kota.getOrDefault("Prancis", "—")}")
    println("getOrNull: ${kota["Prancis"]}")

    // keys, values, entries
    println("Keys: ${kota.keys}")
    println("Values: ${kota.values}")
    println("Entries: ${kota.entries}")

    // Iterasi: insertion order dipertahankan (LinkedHashMap)
    println("Iterasi map:")
    for ((negara, namaKota) in kota) {
        println("  $negara -> $namaKota")
    }

    // Iterasi entries
    println("Iterasi entries:")
    kota.entries.forEach { (k, v) -> println("  $k: $v") }

    // containsKey, containsValue
    println("Ada key \"Jepang\"? ${kota.containsKey("Jepang")}")
    println("Ada value \"Seoul\"? ${kota.containsValue("Seoul")}")

    // Map dengan nilai nullable
    val catatan: Map<String, Int?> = mapOf("a" to 1, "b" to null, "c" to 3)
    println("Nilai b: ${catatan["b"]}")
    println("Nilai d: ${catatan["d"]}")  // key tidak ada -> null

    // Mengonversi list pasangan menjadi map
    val pasangan = listOf("x" to 10, "y" to 20, "z" to 30)
    val dariList: Map<String, Int> = pasangan.toMap()
    println("Map dari list: $dariList")

    // empty map
    val kosong: Map<String, String> = emptyMap()
    println("Map kosong size: ${kosong.size}")
}

Output yang Dihasilkan:

text
Kota Jepang: Tokyo
Kota Prancis: null
getOrElse: (tidak ada)
getOrDefault: —
getOrNull: null
Keys: [Jepang, Korea, Indonesia]
Values: [Tokyo, Seoul, Jakarta]
Entries: [Jepang=Tokyo, Korea=Seoul, Indonesia=Jakarta]
Iterasi map:
  Jepang -> Tokyo
  Korea -> Seoul
  Indonesia -> Jakarta
Iterasi entries:
  Jepang: Tokyo
  Korea: Seoul
  Indonesia: Jakarta
Ada key "Jepang"? true
Ada value "Seoul"? true
Nilai b: null
Nilai d: null
Map dari list: {x=10, y=20, z=30}
Map kosong size: 0

Mari kita bedah baris-baris penting di atas:

  1. kota["Jepang"]: subscript setara dengan kota.get("Jepang"). Mengembalikan "Tokyo" bertipe String?.
  2. kota.getOrElse("Prancis") { "(tidak ada)" }: karena "Prancis" tidak ada sebagai key, lambda dijalankan dan mengembalikan string default. Tipe hasilnya String, bukan String?.
  3. for ((negara, namaKota) in kota): destructuring pada setiap Map.Entry, memberi kita dua variabel bernama negara dan namaKota tanpa harus memanggil entry.key atau entry.value.
  4. catatan["b"] dan catatan["d"]: keduanya mengembalikan null, tetapi dengan sebab berbeda. "b" ada dengan value null; "d" tidak ada sebagai key. Tipe Map<String, Int?> tidak bisa membedakan kedua kasus — perlu containsKey untuk memisahkannya.
  5. pasangan.toMap(): mengubah List<Pair<String, Int>> menjadi Map<String, Int>. Berguna ketika kita membangun pasangan secara dinamis sebelum membekukannya menjadi map.

Referensi Lanjutan #

Setelah menguasai Map read-only, kita akan melihat versi mutable yang memungkinkan kita memperbarui nilai dan menambah key baru saat program berjalan.