Set read-only hanya bisa dibaca; kita tidak bisa menambah atau menghapus elemen setelah dibuat. Ketika koleksi unik perlu diubah seiring waktu — misalnya tag yang dipilih pengguna, ID peserta yang bergabung dan keluar — kita menggunakan MutableSet<T>. Pada chapter ini kita akan mempraktikkan operasi mutasi MutableSet dan memahami perbedaan dua implementasi di baliknya: LinkedHashSet dan HashSet.

Membuat MutableSet #

MutableSet dibuat menggunakan mutableSetOf(...). Pustaka standar Kotlin memilih LinkedHashSet sebagai implementasi default, sehingga urutan penyisipan elemen tetap dipertahankan.

kotlin Contoh: deklarasi MutableSet
val tag: MutableSet<String> = mutableSetOf("kotlin", "jvm", "android")
val angka = mutableSetOf(1, 2, 3)              // MutableSet<Int>
val kosong: MutableSet<Int> = mutableSetOf()

Operasi Mutasi #

MetodeefekCatatan
add(x)menambah x jika belum adaMengembalikan true jika x benar-benar ditambahkan
remove(x)menghapus x jika adaMengembalikan true jika berhasil
addAll(c)menambah semua elemen dari collection cDuplikat diabaikan
removeAll(c)menghapus semua elemen yang ada di cMengembalikan true jika ada perubahan
retainAll(c)menyisakan hanya elemen yang ada di cIrisan in-place
clear()mengosongkan setsize menjadi 0

LinkedHashSet vs HashSet #

Dua implementasi MutableSet di JVM memiliki karakteristik urutan yang berbeda:

ImplementasiCara membuatUrutanPerforma add/contains
LinkedHashSetmutableSetOf, linkedSetOfUrutan penyisipan dipertahankanSedikit lebih lambat (menyimpan daftar tertaut)
HashSethashSetOfTidak menjamin urutan, berdasarkan hash codePaling cepat
TreeSet (sorted)sortedSetOfUrutan natural atau via ComparatorO(log n)

Contoh Lengkap #

kotlin
import java.util.LinkedHashSet

fun main() {
    // MutableSet default = LinkedHashSet (urutan penyisipan dipertahankan)
    val tagMutable = mutableSetOf("kotlin", "jvm", "android")
    println("Awal: $tagMutable")

    // add dan remove
    tagMutable.add("backend")
    println("Setelah add backend: $tagMutable")

    tagMutable.add("kotlin") // sudah ada, tidak ada perubahan
    println("Setelah add kotlin (duplikat): $tagMutable")

    tagMutable.remove("jvm")
    println("Setelah remove jvm: $tagMutable")

    // addAll dan removeAll
    tagMutable.addAll(listOf("web", "mobile", "kotlin"))
    println("Setelah addAll: $tagMutable")

    tagMutable.removeAll(listOf("web", "mobile"))
    println("Setelah removeAll: $tagMutable")

    // retainAll: menyisakan hanya elemen yang ada di argumen
    val angka = mutableSetOf(1, 2, 3, 4, 5)
    angka.retainAll(listOf(2, 4, 6, 8))
    println("Setelah retainAll {2,4,6,8}: ${angka.sorted()}")

    // clear
    val sementara = mutableSetOf("x", "y")
    sementara.clear()
    println("Sementara setelah clear: $sementara, isEmpty=${sementara.isEmpty()}")

    // Perbandingan: LinkedHashSet vs HashSet (urutan)
    val linked: LinkedHashSet<String> = linkedSetOf("zebra", "apel", "mangga")
    println("LinkedHashSet urutan: $linked")

    val hashed: MutableSet<String> = hashSetOf("zebra", "apel", "mangga")
    println("HashSet urutan (hash-based): ${hashed.sorted()}")

    // addAll ke HashSet dengan elemen bertambah
    hashed.add("banana")
    println("HashSet setelah add banana: ${hashed.sorted()}")

    // Konversi ke list untuk akses indeks
    val daftarTag = tagMutable.toList()
    println("Sebagai list indeks 0: ${daftarTag[0]}")
}

Output yang Dihasilkan:

text
Awal: [kotlin, jvm, android]
Setelah add backend: [kotlin, jvm, android, backend]
Setelah add kotlin (duplikat): [kotlin, jvm, android, backend]
Setelah remove jvm: [kotlin, android, backend]
Setelah addAll: [kotlin, android, backend, web, mobile]
Setelah removeAll: [kotlin, android, backend]
Setelah retainAll {2,4,6,8}: [2, 4]
Sementara setelah clear: [], isEmpty=true
LinkedHashSet urutan: [zebra, apel, mangga]
HashSet urutan (hash-based): [apel, mangga, zebra]
HashSet setelah add banana: [apel, banana, mangga, zebra]
Sebagai list indeks 0: kotlin

Mari kita bedah baris-baris penting di atas:

  1. tagMutable.add("kotlin"): pemanggilan ini tidak menambah elemen karena "kotlin" sudah ada. add mengembalikan false dan size tetap sama. Perilaku ini berbeda dari MutableList.add yang selalu menambah elemen.
  2. tagMutable.removeAll(listOf("web", "mobile")): menghapus elemen apa pun yang juga ada di argumen. Hasilnya adalah tagMutable hanya berisi elemen yang bukan anggota list.
  3. angka.retainAll(listOf(2, 4, 6, 8)): mempertahankan elemen yang ada di kedua koleksi. Hanya 2 dan 4 yang tersisa karena 6 dan 8 tidak ada di set awal.
  4. linked: LinkedHashSet<String>: urutan [zebra, apel, mangga] dipertahankan persis seperti urutan penyisipan.
  5. hashed.sorted(): HashSet tidak menjamin urutan, sehingga kita memanggil sorted() agar output konsisten lintas platform dan versi JVM.

Referensi Lanjutan #

Setelah memahami Set dan MutableSet, kita akan melangkah ke struktur pasangan key-value: Map.