Set adalah collection yang tidak menerima duplikat. Ketika kita perlu menyimpan nilai unik seperti kode akses, daftar ID pengguna, atau tag artikel, Set adalah pilihan yang tepat. Pada chapter ini kita akan membuat set, memeriksa keanggotaan elemen, dan menerapkan operasi himpunan klasik: union, intersect, dan subtract.

Karakteristik Set #

Set<T> memiliki dua jaminan utama:

  1. Tidak ada duplikat — pemanggilan setOf("A", "A", "B") menghasilkan set berisi "A" dan "B" saja.
  2. Tanpa jaminan urutan — implementasi LinkedHashSet mempertahankan urutan penyisipan, sedangkan HashSet tidak.

Pustaka standar Kotlin memilih LinkedHashSet sebagai implementasi default setOf, sehingga urutan penyisipan tetap terjaga. Tetapi kontrak Set sendiri tidak menjamin urutan tertentu, jadi kita perlu berhati-hati saat menulis kode yang bergantung pada urutan.

Membuat Set Read-Only #

kotlin Contoh: deklarasi Set
val buah: Set<String> = setOf("Apel", "Jeruk", "Pisang")
val angka = setOf(1, 2, 2, 3)           // Set<Int>: {1, 2, 3}
val kosong: Set<Int> = emptySet()

Karena Set tidak punya indeks, kita tidak bisa menulis set[0]. Untuk memeriksa keanggotaan, gunakan contains(x) atau operator in.

Operasi Himpunan #

Set mendukung operasi himpunan klasik:

OperasiFungsiHasil
Uniona.union(b)Elemen yang ada di a atau b (atau keduanya)
Intersecta.intersect(b)Elemen yang ada di a dan b sekaligus
Subtracta.subtract(b)Elemen yang ada di a tetapi tidak di b
Subseta.containsAll(b)Apakah semua elemen b ada di a

union, intersect, dan subtract mengembalikan Set baru; collection asli tidak berubah. Operasi union juga menerima koleksi lain yang bukan Set (misalnya List) karena konsepnya berlaku universal.

Contoh Lengkap #

Berikut program yang memperagakan pembuatan, pemeriksaan, dan operasi himpunan:

kotlin
fun main() {
    val kodeAkses = setOf("A1", "B2", "A1", "C3")
    println("Set kode akses: ${kodeAkses.sorted()}")

    // Duplikat diabaikan
    val buah = setOf("Apel", "Jeruk", "Apel", "Pisang", "Jeruk")
    println("Set buah (duplikat hilang): ${buah.sorted()}")

    // Ukuran setelah penghapusan duplikat
    println("Jumlah buah unik: ${buah.size}")

    // contains dan in
    println("Apakah \"Pisang\" ada? ${buah.contains("Pisang")}")
    println("Apakah \"Mangga\" ada? ${"Mangga" in buah}")

    // Operasi himpunan: union, intersect, subtract
    val timA = setOf("Budi", "Siti", "Dewi", "Andi")
    val timB = setOf("Dewi", "Andi", "Rian", "Putri")

    val gabungan = timA.union(timB)
    println("Union: ${gabungan.sorted()}")

    val irisan = timA.intersect(timB)
    println("Intersect: ${irisan.sorted()}")

    val selisihA = timA.subtract(timB)
    println("A - B: ${selisihA.sorted()}")

    val selisihB = timB.subtract(timA)
    println("B - A: ${selisihB.sorted()}")

    // symmetricDifference (ekstensi stdlib)
    val symDiff = timA.filter { it !in timB } + timB.filter { it !in timA }
    println("Symmetric difference: ${symDiff.sorted()}")

    // Subset dan superset
    val kecil = setOf("Budi", "Siti")
    println("kecil subsetOf timA? ${kecil.containsAll(timA)}")
    println("timA containsAll kecil? ${timA.containsAll(kecil)}")

    // Set elemen nullable
    val denganNull: Set<String?> = setOf("a", null, "a", "b")
    println("Set dengan null (sorted): ${denganNull.toList().sortedBy { it ?: "" }}")
}

Output yang Dihasilkan:

text
Set kode akses: [A1, B2, C3]
Set buah (duplikat hilang): [Apel, Jeruk, Pisang]
Jumlah buah unik: 3
Apakah "Pisang" ada? true
Apakah "Mangga" ada? false
Union: [Andi, Budi, Dewi, Putri, Rian, Siti]
Intersect: [Andi, Dewi]
A - B: [Budi, Siti]
B - A: [Putri, Rian]
Symmetric difference: [Budi, Putri, Rian, Siti]
kecil subsetOf timA? false
timA containsAll kecil? true
Set dengan null (sorted): [null, a, b]

Mari kita bedah baris-baris penting di atas:

  1. kodeAkses.sorted(): Set default menggunakan LinkedHashSet sehingga urutan penyisipan dipertahankan. Memanggil sorted() memastikan output selalu deterministik di berbagai implementasi.
  2. buah.size: jumlah elemen unik. Walaupun literal setOf menulis "Apel" dua kali, hanya satu yang disimpan.
  3. timA.union(timB): menggabungkan kedua tim tanpa duplikat. Elemen yang muncul di kedua set hanya disimpan sekali.
  4. timA.intersect(timB): hanya elemen yang ada di kedua set — "Dewi" dan "Andi" — yang masuk ke hasil.
  5. timA.filter { it !in timB } + ...: membentuk symmetric difference (elemen yang hanya ada di salah satu set) secara manual. Kotlin tidak menyediakan fungsi bawaan untuk operasi ini.
  6. timA.containsAll(kecil): memeriksa apakah timA adalah superset dari kecil. Mengembalikan true karena kedua nama di kecil juga ada di timA.

Referensi Lanjutan #

Setelah memahami Set read-only, kita akan membahas versi mutable pada chapter berikutnya yang memungkinkan penambahan dan penghapusan elemen seiring waktu.