Kotlin menyediakan tiga antarmuka utama untuk menampung banyak nilai dalam satu objek, yaitu List, Set, dan Map. Ketiganya hadir dalam dua varian, yakni antarmuka read-only yang hanya menyediakan operasi pembacaan dan antarmuka mutable yang mengizinkan perubahan isi. Topik ini membahas cara membuat collection dengan listOf, setOf, mapOf, beserta padanan mutable-nya, akses elemen pada tiap tipe, serta perilaku error saat kunci yang dicari pada Map tidak ada. Seluruh contoh dijalankan dengan compiler Kotlin sehingga keluarannya berasal dari eksekusi nyata.
List untuk daftar berurutan#
List menyimpan elemen dalam urutan tertentu, dan setiap elemen diakses melalui indeksnya yang dimulai dari nol. Antarmuka List hanya menyediakan operasi pembacaan, yaitu mengambil elemen, membaca jumlah elemen lewat properti size, dan memeriksa keberadaan nilai dengan contains. Fungsi listOf membentuk objek List dari argumen-argumen yang dikirim kepadanya. Nilai yang sama boleh muncul lebih dari satu kali pada List. Program berikut membaca daftar nilai ujian dengan beberapa operasi tersebut.
fun main() {
val nilai = listOf(80, 65, 90, 75)
println(nilai)
println(nilai[0])
println(nilai.size)
println(nilai.contains(90))
}# compiler Kotlin dan pustaka standar yang dipakai jalur ini
export PATH=/home/kal/my-project/tools/kotlinc/bin:/home/kal/my-project/tools/jdk-merged/bin:$PATH
export STDLIB=/home/kal/my-project/tools/kotlinc/lib/kotlin-stdlib.jar
# kompilasi daftar.kt menjadi daftar.jar, lalu jalankan programnya
kotlinc daftar.kt -d daftar.jar && java -cp "daftar.jar:$STDLIB" DaftarKt[80, 65, 90, 75]
80
4
truePemanggilan nilai[0] menghasilkan 80 karena indeks pertama pada List adalah 0, sedangkan nilai.size menghasilkan 4 karena daftar memuat empat elemen. Properti size dan fungsi contains bekerja tanpa mengubah isi daftar, sesuai kontrak antarmuka read-only. Apabila indeks yang diminta berada di luar jangkauan, misalnya nilai[10], akses tersebut menghasilkan error IndexOutOfBoundsException saat program berjalan.
Set tanpa duplikat#
Set menampung setiap nilai paling banyak satu kali. Antarmuka ini digunakan ketika keunikan anggota lebih penting daripada jumlah kemunculan, misalnya daftar nama yang sudah mengisi absensi. Fungsi setOf membentuk Set dari argumennya, dan nilai yang dikirim dua kali hanya dicatat satu kali tanpa menimbulkan error. Program berikut mengirim nama Ali dua kali dan memeriksa hasilnya.
fun main() {
val hadir = setOf("Ali", "Budi", "Ali", "Citra")
println(hadir)
println(hadir.size)
println(hadir.contains("Budi"))
}# kompilasi himpun.kt menjadi himpun.jar, lalu jalankan programnya
kotlinc himpun.kt -d himpun.jar && java -cp "himpun.jar:$STDLIB" HimpunKt[Ali, Budi, Citra]
3
trueKeluaran memuat Ali, Budi, dan Citra, sedangkan kemunculan Ali yang kedua dihilangkan tanpa pesan apa pun. Properti size menghasilkan 3, bukan 4, karena duplikat tidak ikut dihitung. Urutan keluaran mengikuti urutan kemampukan nilai, sehingga hasil tetap dapat diprediksi pada data dengan jumlah kecil. Ketika program membutuhkan keunikan anggota sekaligus urutan yang stabil, setOf memenuhi keduanya sebagaimana terlihat di sini.
Map untuk pasangan kunci dan nilai#
Map menyimpan pasangan kunci dan nilai, dan setiap kunci hanya boleh muncul satu kali. Antarmuka ini dipakai ketika setiap data dicari melalui penandanya, misalnya nilai ujian yang dicari melalui nama siswa. Fungsi mapOf menerima pasangan dalam bentuk "Ali" to 80, yaitu pemanggilan fungsi infix to yang membentuk objek Pair. Pengambilan nilai dilakukan dengan operator [] atau dengan fungsi getValue. Program berikut membandingkan keduanya.
fun main() {
val nilai = mapOf("Ali" to 80, "Budi" to 90, "Citra" to 75)
println(nilai)
println(nilai["Budi"])
println(nilai["Dewi"])
println(nilai.getValue("Citra"))
}# kompilasi nilai.kt menjadi nilai.jar, lalu jalankan programnya
kotlinc nilai.kt -d nilai.jar && java -cp "nilai.jar:$STDLIB" NilaiKt{Ali=80, Budi=90, Citra=75}
90
null
75Operator [] pada Map menghasilkan tipe nullable, yaitu Int? untuk contoh ini, sehingga kunci Dewi yang tidak ada menghasilkan null dan bukan error. Sebaliknya, getValue menyatakan kunci sebagai sesuatu yang wajib ada, sehingga hasilnya bertipe Int tanpa tanda tanya dan program berhenti apabila kunci tidak ditemukan. Pemilihan antara keduanya mengikuti kepastian data, yaitu [] untuk kunci yang mungkin absen dan getValue untuk kunci yang dianggap pasti. Keluaran baris pertama juga memperlihatkan urutan pasangan mengikuti urutan penulisannya.
Kunci yang tidak ada#
Kesalahan pencarian kunci pada Map memunculkan error saat program berjalan, bukan saat kompilasi, karena keberadaan kunci baru diketahui dari isi data. Fungsi getValue adalah contoh yang paling langsung, dan program berikut sengaja mencari kunci Citra pada Map yang hanya memuat Ali dan Budi.
fun main() {
val nilai = mapOf("Ali" to 80, "Budi" to 90)
println(nilai.getValue("Citra"))
}# kompilasi kunci.kt lalu jalankan programnya untuk melihat error saat berjalan
kotlinc kunci.kt -d kunci.jar && java -cp "kunci.jar:$STDLIB" KunciKtException in thread "main" java.util.NoSuchElementException: Key Citra is missing in the map.
at kotlin.collections.MapsKt__MapWithDefaultKt.getOrImplicitDefaultNullable(MapWithDefault.kt:24)
at kotlin.collections.MapsKt__MapsKt.getValue(Maps.kt:370)
at KunciKt.main(kunci.kt:3)
at KunciKt.main(kunci.kt)Pesan error NoSuchElementException: Key Citra is missing in the map menyebut kunci yang tidak ditemukan, sedangkan baris-baris berawalan at merupakan pelacakan tumpukan (stack trace) yang menunjuk letak kejadiannya, yaitu pemanggilan getValue pada baris 3 berkas kunci.kt. Perbaikannya mengikuti maksud program, yaitu menggunakan [] bila kunci memang boleh tidak ada, atau memeriksa kunci dengan containsKey terlebih dahulu sebelum nilainya diambil.
Varian mutable#
Setiap antarmuka memiliki padanan mutable, yaitu MutableList, MutableSet, dan MutableMap, yang dibuat dengan mutableListOf, mutableSetOf, dan mutableMapOf. Ketiganya menyediakan operasi pengubahan seperti add, remove, dan penugasan lewat []. Sisanya sama dengan versi read-only, sehingga aturan indeks, keunikan, dan kunci yang sudah dibahas tetap berlaku. Program berikut mengubah isi ketiga tipe mutable sekaligus.
fun main() {
val belanja = mutableListOf("beras", "gula")
belanja.add("minyak")
belanja.remove("gula")
println(belanja)
val piket = mutableMapOf("Senin" to "Ali", "Selasa" to "Budi")
piket["Rabu"] = "Citra"
piket["Senin"] = "Dewi"
println(piket)
val hadir = mutableSetOf("Ali", "Budi")
hadir.add("Citra")
hadir.add("Ali")
println(hadir)
}# kompilasi ubah.kt menjadi ubah.jar, lalu jalankan programnya
kotlinc ubah.kt -d ubah.jar && java -cp "ubah.jar:$STDLIB" UbahKt[beras, minyak]
{Senin=Dewi, Selasa=Budi, Rabu=Citra}
[Ali, Budi, Citra]Blok pertama menambah minyak lalu menghapus gula, sehingga daftar belanja tersisa [beras, minyak]. Blok kedua memperlihatkan dua peran penugasan [] pada Map mutable, yaitu piket["Rabu"] = "Citra" yang menambah pasangan baru dan piket["Senin"] = "Dewi" yang menimpa nilai kunci yang sudah ada. Blok ketiga menunjukkan add untuk Ali yang diabaikan karena Set tidak menerima duplikat. Dengan tipe mutable, isi collection berubah sepanjang program berjalan tanpa perlu membuat objek baru.
Tampilan read-only bukan jaminan immutable#
Antarmuka read-only hanya membatasi operasi yang terlihat melalui variabel yang bertipe read-only, dan tidak menjamin isi objeknya tidak pernah berubah. Objek mutable dapat diberikan ke variabel bertipe List, dan perubahan melalui referensi mutable tetap terlihat dari tampilan tersebut. Program berikut membuktikannya.
fun main() {
val dataMentah = mutableListOf(10, 20, 30)
val tampilan: List<Int> = dataMentah
dataMentah.add(40)
println(tampilan)
}# kompilasi tampilan.kt menjadi tampilan.jar, lalu jalankan programnya
kotlinc tampilan.kt -d tampilan.jar && java -cp "tampilan.jar:$STDLIB" TampilanKt[10, 20, 30, 40]Keluaran [10, 20, 30, 40] memperlihatkan tampilan read-only ikut berubah ketika add(40) dipanggil melalui dataMentah, karena tampilan dan dataMentah menunjuk objek yang sama. Pembatasan read-only hanya menghilangkan operasi pengubahan dari tipe yang terlihat, sedangkan keputusan tentang kebekuan isi berada pada objek dasarnya. Sisi lain dari pembatasan yang sama bersifat statis, yaitu pemanggilan add pada hasil listOf ditolak sejak waktu kompilasi sebagaimana berikut.
fun main() {
val angka = listOf(1, 2, 3)
angka.add(4)
}# kompilasi tambah.kt untuk melihat penolakan dari compiler
kotlinc tambah.kt -d tambah.jartambah.kt:3:11: error: unresolved reference 'add'.
angka.add(4)
^^^Pesan error unresolved reference 'add' muncul karena antarmuka List tidak memiliki operasi bernama add, sehingga pemanggilan itu tidak pernah sampai ke tahap eksekusi. Dua perilaku di atas berdiri berdampingan, yaitu operasi pengubah tidak tersedia pada tipe read-only, dan isi yang tampak read-only tetap dapat berubah dari referensi mutable lain. Keduanya menjelaskan mengapa istilah read-only dipilih alih-alih immutable.
Ringkasan#
List,Set, danMapmasing-masing memiliki pasangan antarmuka read-only dan mutable, yaituMutableList,MutableSet, danMutableMap.- Fungsi
listOf,setOf, danmapOfmenghasilkan objek read-only, sedangkanmutableListOf,mutableSetOf, danmutableMapOfmengizinkanadd,remove, dan penugasan elemen. - Operator
[]padaMapmenghasilkan tipe nullable, sedangkangetValuemenghasilkan nilai langsung dan melemparNoSuchElementExceptionbila kunci tidak ada. - Antarmuka read-only bukan jaminan immutability, karena isi objek tetap dapat berubah melalui referensi mutable yang menunjuk objek yang sama.
Referensi#
- Collections overview — Kotlin Documentation
— Struktur
List,Set, danMapbeserta varian read-only dan mutable pada dokumentasi resmi Kotlin. - Map-specific operations — Kotlin Documentation
— Cara membaca dan mengubah
Map, termasuk operator[],getValue, dancontainsKey.