dartsetcollectionMenengah3 mnt baca

Set

Koleksi unik tanpa duplikat: kapan memakai Set dan operasinya.

Koleksi tanpa duplikat

List boleh berisi nilai kembar: ['apel', 'apel'] valid. Tapi kadang duplikat itu masalah: daftar email undangan, tag artikel, ID pengguna. Set adalah koleksi yang otomatis menolak nilai duplikat.

Analogi: list itu seperti daftar hadir yang bisa ditandatangani dua kali oleh orang yang sama. Set itu seperti cap stempel "sudah hadir", dicap dua kali pun tetap satu.

Kenapa Set ada?

Tiga kegunaan utama: menghilangkan duplikat, mengecek keanggotaan super cepat, dan operasi himpunan (gabungan, irisan). Untuk daftar yang urutannya penting dan boleh kembar, tetap pakai list. Untuk "kumpulan unik", pakai set.

Membuat dan memakai Set

dart
void main() {
  // Set literal memakai kurung kurawal
  var buah = {'apel', 'mangga', 'pisang'};

  print(buah); // {apel, mangga, pisang}

  buah.add('apel'); // duplikat: DITOLAK diam-diam
  print(buah); // {apel, mangga, pisang}, tetap 3!

  buah.add('durian');
  print(buah); // {apel, mangga, pisang, durian}

  print(buah.contains('mangga')); // true, pengecekan sangat cepat
  print(buah.length); // 4
}

Perhatikan: menambah 'apel' kedua kalinya tidak error, tapi juga tidak mengubah apa-apa. Set diam-diam menjaga keunikannya.

Contoh kedua, membersihkan duplikat dari list:

dart
void main() {
  var email = [
    '[email protected]',
    '[email protected]',
    '[email protected]',
    '[email protected]',
    '[email protected]',
  ];

  // Trik klasik: List -> Set -> List
  var unik = email.toSet().toList();
  print(unik); // [[email protected], [email protected], [email protected]]
}

.toSet() membuang duplikat dalam satu langkah. Ini pola yang akan sering kamu pakai saat membersihkan data.

Hati-hati: Set vs Map literal

Keduanya memakai {}, dan ini jebakan bagi pemula:

dart
void main() {
  var a = {'apel', 'mangga'}; // SET: isinya nilai saja
  var b = {'nama': 'Budi'};  // MAP: isinya pasangan key: value

  print(a.runtimeType); // _Set<String>
  print(b.runtimeType); // _Map<String, String>

  // Set kosong butuh anotasi eksplisit!
  var kosong = <String>{}; // Set<String> kosong
  // var ambigu = {}; // ini Map, bukan Set!
  print(kosong);
}

Dart menebak dari isi: kalau ada titik dua (:), itu Map. {} kosong selalu dianggap Map, jadi set kosong wajib ditulis <String>{}.

Kesalahan umum

1. Set kosong tanpa anotasi tipe

dart
// SALAH: dikira Set, ternyata Map
void main() {
  var s = {};
  // s.add('apel'); // ERROR: Map tidak punya add
  print(s.runtimeType); // _Map<dynamic, dynamic>
}
dart
// BENAR
void main() {
  var s = <String>{};
  s.add('apel');
  print(s); // {apel}
}

2. Mengharapkan urutan seperti list

dart
void main() {
  var s = {'c', 'a', 'b'};
  print(s); // {c, a, b}, urutan tidak dijamin bermakna!
  // print(s[0]); // ERROR: Set tidak punya index []
}

Set tidak mendukung akses index [0]. Kalau butuh elemen pertama, pakai .first. Kalau urutan penting, pertimbangkan list.

3. Objek custom dianggap duplikat secara salah

dart
class Siswa {
  String nama;
  Siswa(this.nama);
}

void main() {
  var s = <Siswa>{};
  s.add(Siswa('Budi'));
  s.add(Siswa('Budi')); // dianggap BEDA objek, keduanya masuk!
  print(s.length); // 2
}

Set memakai == dan hashCode untuk menilai duplikat. Objek custom yang tidak meng-override keduanya dianggap unik walau isinya sama. Solusinya dibahas di modul override equality.

Kesimpulan

Set = koleksi unik tanpa duplikat, ditulis dengan {}, dibuat kosong dengan <T>{}. Pakai untuk deduplikasi (.toSet()) dan pengecekan keanggotaan cepat. Ingat bedanya dengan Map literal.

Catatan teknis: Set.contains berjalan dalam waktu konstan O(1) berkat hash table di baliknya, sedangkan List.contains memindai satu per satu O(n). Untuk pengecekan berulang pada data besar, perbedaannya sangat terasa.

Tantangan

Pembersih duplikat

Diberi list email dengan duplikat, ubah jadi Set untuk dapat email unik, cetak jumlah uniknya, lalu ubah kembali jadi List.

text
void main() {
  var email = ['[email protected]', '[email protected]', '[email protected]', '[email protected]', '[email protected]'];
  // TODO
}