Pemrograman Dart
Koleksi Data: List, Set, Map, dan Collection Operators
Pelajari cara mengelola kumpulan data di Dart 3 menggunakan List, Set, Map, dan Record sederhana, lengkap dengan spread operator (...), collection if, serta collection for.

Pada bab pertama, kita telah mempelajari cara menyimpan satu nilai ke dalam satu variabel. Namun dalam aplikasi nyata, data hampir tidak pernah berdiri sendiri: sebuah toko daring menampilkan ratusan produk di halaman katalog, sistem akademik menyimpan daftar unik NIM mahasiswa yang hadir, dan pengaturan aplikasi menyimpan pasangan kode bahasa beserta labelnya. Mendeklarasikan variabel produk1, produk2, hingga produk100 tentu tidak masuk akal. Untuk mengelola banyak data sekaligus secara terstruktur, Dart menyediakan tiga struktur koleksi data utama—List, Set, dan Map—ditambah tipe pengelompokan ringan Record di Dart 3 serta operator koleksi deklaratif yang sangat mempermudah penyusunan antarmuka Flutter.
Yang akan dipelajari
- Membedakan karakteristik dan kegunaan
List<T>,Set<T>, danMap<K, V>. - Membuat, mengakses, menambah, mengubah, dan menghapus elemen pada
List,Set, danMapdengan tipe data yang aman (type-safe). - Mengelompokkan beberapa nilai berbeda tipe secara ringkas menggunakan fitur Record di Dart 3.
- Menggabungkan dan membangun koleksi secara dinamis menggunakan spread operator (
...dan...?),collection if, dancollection for.
Perbandingan List, Set, dan Map
Sebelum memilih struktur koleksi, tanyakan pada diri Anda bagaimana data tersebut akan diakses dan apakah duplikasi diperbolehkan:
| Koleksi | Sintaks Literal | Ciri Utama | Contoh Kasus Penggunaan |
|---|---|---|---|
List<T> |
[a, b, c] |
Berurutan berdasarkan indeks (0, 1, 2...), boleh ada nilai kembar/duplikat |
Daftar riwayat pesanan, antrean tugas, daftar berita |
Set<T> |
{a, b, c} |
Setiap elemen wajib unik (otomatis menolak duplikat), mendukung operasi himpunan | Daftar tag artikel, kumpulan ID pengguna unik, daftar izin akses |
Map<K, V> |
{k1: v1, k2: v2} |
Menyimpan pasangan Kunci (Key) unik dan Nilai (Value) | Kamus kode provinsi, data JSON, profil pengaturan aplikasi |
Huruf <T> atau <K, V> di dalam kurung siku lancip menunjukkan tipe elemen (generics) yang boleh masuk ke dalam koleksi tersebut. Misalnya, List<String> hanya mengizinkan elemen bertipe String.
1. List<T>: Daftar Berurutan Berbasis Indeks
List menyimpan data secara berurutan. Elemen pertama selalu berada di indeks ke-0, dan elemen terakhir berada di indeks panjangList - 1.
void main() {
// Mendeklarasikan List bertipe String
List<String> antreanPasien = ['Andi', 'Budi', 'Citra'];
// Mengakses elemen menggunakan indeks [0], [1], dst.
print('Pasien pertama : ${antreanPasien[0]}');
print('Jumlah antrean : ${antreanPasien.length} orang');
// Menambah elemen baru di akhir list
antreanPasien.add('Dewi');
// Menyisipkan elemen di indeks ke-1 (menggeser elemen setelahnya)
antreanPasien.insert(1, 'Eka (Darurat)');
// Mengubah nilai elemen pada indeks tertentu
antreanPasien[0] = 'Andi Wijaya';
// Menghapus elemen berdasarkan nilainya
antreanPasien.remove('Budi');
print('Antrean terbaru: $antreanPasien');
}
// Output:
// Pasien pertama : Andi
// Jumlah antrean : 3 orang
// Antrean terbaru: [Andi Wijaya, Eka (Darurat), Citra, Dewi]
Beberapa properti dan method List yang paling sering dipakai antara lain .first (elemen pertama), .last (elemen terakhir), .isEmpty (mengecek apakah kosong), dan .contains(nilai) (mengecek keberadaan elemen).
2. Set<T>: Himpunan Elemen Unik
Set ditulis menggunakan kurung kurawal {...}. Berbeda dengan List, Set menjamin tidak akan pernah ada dua elemen yang bernilai sama. Jika kita mencoba memasukkan nilai yang sudah ada, operasi tersebut akan diabaikan secara otomatis tanpa menimbulkan error. Selain itu, Set menyediakan operasi matematika himpunan seperti irisan (intersection), gabungan (union), dan selisih (difference).
void main() {
// Membuat Set dengan kurung kurawal {}
Set<String> pesertaWorkshopDart = {'Rina', 'Fajar', 'Dimas', 'Rina'};
// Perhatikan bahwa 'Rina' yang kedua otomatis diabaikan!
print('Peserta Dart : $pesertaWorkshopDart');
Set<String> pesertaWorkshopFlutter = {'Fajar', 'Sinta', 'Bagas'};
// Menambah dan menghapus elemen
pesertaWorkshopDart.add('Putra');
pesertaWorkshopDart.remove('Dimas');
// Operasi Himpunan:
// 1. Irisan (peserta yang mengikuti KEDUA workshop)
Set<String> ikutKeduanya = pesertaWorkshopDart.intersection(pesertaWorkshopFlutter);
// 2. Gabungan (seluruh peserta unik dari kedua workshop)
Set<String> semuaPeserta = pesertaWorkshopDart.union(pesertaWorkshopFlutter);
print('Ikut Keduanya : $ikutKeduanya');
print('Semua Peserta : $semuaPeserta');
}
// Output:
// Peserta Dart : {Rina, Fajar, Dimas}
// Ikut Keduanya : {Fajar}
// Semua Peserta : {Rina, Fajar, Putra, Sinta, Bagas}
Trik Praktis: Jika Anda memiliki
Listyang mengandung banyak data ganda (duplikat) dan ingin membersihkannya dengan cepat, cukup panggildaftarGanda.toSet().toList().
3. Map<K, V>: Pasangan Kunci dan Nilai (Key-Value)
Pada List, kita mencari data menggunakan nomor urut indeks (0, 1, 2). Namun bagaimana jika kita ingin mencari harga produk berdasarkan kode barangnya ('PRD-01'), atau mencari nilai mahasiswa berdasarkan NIM-nya? Gunakan Map<K, V>, di mana K adalah tipe Key (kunci unik) dan V adalah tipe Value (nilai).
Perhatikan satu hal penting terkait Null Safety saat mengakses Map: mengambil nilai dengan map[kunci] akan mengembalikan tipe nullable (V?), karena Dart mengantisipasi kemungkinan kunci yang kita cari ternyata tidak ada di dalam Map!
void main() {
// Map dengan Key bertipe String dan Value bertipe int
Map<String, int> stokGudang = {
'Kopi Arabika': 25,
'Teh Hijau': 40,
'Gula Aren': 15,
};
// Mengakses nilai berdasarkan kunci (menghasilkan int?, gunakan ?? untuk nilai cadangan)
int stokKopi = stokGudang['Kopi Arabika'] ?? 0;
int stokSusu = stokGudang['Susu Oat'] ?? 0; // Kunci belum ada, menghasilkan null -> 0
print('Stok Kopi Arabika : $stokKopi bungkus');
print('Stok Susu Oat : $stokSusu bungkus');
// Menambah pasangan baru atau memperbarui nilai kunci yang sudah ada
stokGudang['Cokelat Bubuk'] = 30; // Menambah kunci baru
stokGudang['Gula Aren'] = 20; // Memperbarui nilai kunci lama
// Menelusuri seluruh isi Map menggunakan .entries
print('\n--- Daftar Inventaris Gudang ---');
for (final item in stokGudang.entries) {
print('- ${item.key}: ${item.value} bungkus');
}
}
// Output:
// Stok Kopi Arabika : 25 bungkus
// Stok Susu Oat : 0 bungkus
//
// --- Daftar Inventaris Gudang ---
// - Kopi Arabika: 25 bungkus
// - Teh Hijau: 40 bungkus
// - Gula Aren: 20 bungkus
// - Cokelat Bubuk: 30 bungkus
4. Pengenalan Record di Dart 3
Terkadang kita hanya ingin mengelompokkan 2 atau 3 nilai berbeda tipe (misalnya titik koordinat (lintang, bujur) atau pasangan (kodeStatus, pesan)) tanpa perlu repot membuat Map atau mendefinisikan class baru. Di Dart 3, kita dapat menggunakan Record yang ditulis di dalam tanda kurung biasa (...):
void main() {
// 1. Positional Record: diakses menggunakan .$1, .$2, dst.
(int, String) hasilUjian = (92, 'Lulus dengan Pujian');
print('Skor: ${hasilUjian.$1} | Keterangan: ${hasilUjian.$2}');
// 2. Named Record: memiliki nama field yang jelas dan mudah dibaca
({String kode, int harga, bool tersedia}) infoProduk = (
kode: 'MN-01',
harga: 28000,
tersedia: true,
);
print('Produk ${infoProduk.kode}: Rp${infoProduk.harga} (Stok: ${infoProduk.tersedia})');
}
// Output:
// Skor: 92 | Keterangan: Lulus dengan Pujian
// Produk MN-01: Rp28000 (Stok: true)
Record bersifat immutable (nilainya tetap) dan sepenuhnya type-safe, menjadikannya sangat berguna ketika nanti kita mempelajari fungsi yang perlu mengembalikan lebih dari satu nilai sekaligus.
5. Operator Koleksi: Spread (...), collection if, dan collection for
Salah satu fitur terbaik Dart yang membuat penulisan kode terasa modern dan deklaratif adalah kemampuannya menyisipkan operator logika dan perulangan langsung di dalam deklarasi koleksi:
- Spread Operator (
...dan...?): Membongkar seluruh elemen dari satu koleksi untuk dimasukkan ke dalam koleksi lain. Gunakan...?(null-aware spread) jika koleksi sumbernya mungkin bernilainull. collection if: Menyertakan suatu elemen ke dalam koleksi hanya jika kondisi bernilaitrue.collection for: Membangkitkan banyak elemen di dalam koleksi menggunakan perulanganfor.
void main() {
List<String> menuUtama = ['Beranda', 'Katalog', 'Keranjang'];
List<String>? menuTambahan = ['Riwayat Pesanan', 'Voucher Saya'];
bool adalahAdmin = true;
// Menggabungkan spread (...), null-aware spread (...?), dan collection if
List<String> navigasiLengkap = [
...menuUtama,
...?menuTambahan,
if (adalahAdmin) 'Panel Admin',
'Pengaturan',
];
print('Menu Navigasi : $navigasiLengkap');
// Menggunakan collection for untuk mengubah daftar harga menjadi label diskon
List<int> hargaAsli = [100000, 150000, 200000];
List<String> labelPromo = [
for (final harga in hargaAsli) 'Rp${harga - 20000} (Hemat 20rb)',
];
print('Label Promo : $labelPromo');
}
// Output:
// Menu Navigasi : [Beranda, Katalog, Keranjang, Riwayat Pesanan, Voucher Saya, Panel Admin, Pengaturan]
// Label Promo : [Rp80000 (Hemat 20rb), Rp130000 (Hemat 20rb), Rp180000 (Hemat 20rb)]
Coba sendiri
Buka dartpad.dev dan salin contoh kode tentang Operator Koleksi di atas. Lakukan eksperimen berikut:
- Ubah variabel
bool adalahAdmin = true;menjadifalse, dan ubahmenuTambahanmenjadinull. Klik Run dan perhatikan bagaimana'Panel Admin'serta seluruh isimenuTambahanotomatis tidak dimasukkan ke dalamnavigasiLengkaptanpa menyebabkan error sedikit pun! - Coba ubah sebuah
const List<String> daftarTetap = ['A', 'B'];dengan memanggildaftarTetap.add('C');. Amati UnsupportedError: Cannot add to an unmodifiable list yang muncul saat program dijalankan.
Kesalahan yang sering terjadi
| Kesalahan Pemula | Penyebab | Solusi yang Benar |
|---|---|---|
Menulis var himpunan = {}; ketika ingin membuat Set kosong |
Di Dart, kurung kurawal kosong {} secara default dianggap sebagai Map<dynamic, dynamic>, bukan Set. |
Tuliskan tipe elemennya secara eksplisit: var himpunan = <String>{}; atau Set<String> himpunan = {};. |
Mengakses indeks di luar batas List (misal mengakses list[3] pada list berisi 3 elemen) |
Lupa bahwa indeks List dimulai dari 0, sehingga 3 elemen memiliki indeks 0, 1, dan 2. |
Pastikan indeks selalu < list.length, atau gunakan .first dan .last. |
Langsung menggunakan hasil map['kunci'] sebagai tipe non-nullable tanpa pengecekan |
Mengira kompilator tahu bahwa kunci tersebut pasti ada di dalam Map. |
Berikan nilai cadangan dengan map['kunci'] ?? nilaiDefault atau periksa dulu apakah hasilnya != null. |
Memanggil .add() atau .remove() pada koleksi yang dibuat dengan kata kunci const |
Koleksi const (seperti const [1, 2, 3]) bersifat unmodifiable (tidak boleh dimodifikasi isinya saat runtime). |
Gunakan final (bukan const) jika Anda hanya ingin mengunci referensi variabelnya tetapi masih ingin menambah/menghapus elemen di dalamnya. |
Kaitannya dengan Flutter / Dunia Nyata: Di Flutter, hampir setiap tata letak seperti
Column(children: [...])atauRow(children: [...])menerimaList<Widget>. Berkatcollection ifdan spread operator (...), kita dapat menulis kode tampilan seperti:children: [ JudulHalaman(), if (sedangMemuat) IndikatorLoading(), ...daftarKartuProduk ]Fitur inilah yang membuat penyusunan antarmuka dinamis di Flutter terasa jauh lebih bersih dibandingkan framework lain.
Latihan
Kerjakan tiga soal praktik berikut di DartPad untuk memantapkan penguasaan koleksi data:
- Latihan 1 (Pembersihan Email Duplikat dengan
ListdanSet): Sebuah formulir pendaftaran webinar menerima daftar email berikut:List<String> pendaftarMentah = ['andi@mail.com', 'budi@mail.com', 'andi@mail.com', 'citra@mail.com', 'budi@mail.com'];. Konversikan daftar tersebut menjadiSet<String>untuk menghapus duplikat, tambahkan satu email baru'dewi@mail.com', lalu tampilkan berapa jumlah peserta unik yang terdaftar beserta daftarnya. - Latihan 2 (Kasir Kantin dengan
Map): BuatlahMap<String, int> daftarMenu = {'Nasi Goreng': 18000, 'Mie Ayam': 15000, 'Es Jeruk': 6000};. Seorang pelanggan memesan'Nasi Goreng','Es Jeruk', dan'Kopi Hitam'. Hitung total harga pesanan tersebut menggunakan perulanganfor-in. Jika menu yang dipesan tidak ada di dalamdaftarMenu(null), tampilkan pesan bahwa menu tersebut habis dan anggap harganya0. - Latihan 3 (Menyusun Keranjang Belanja dengan Collection Operators): Diketahui
List<String> barangInti = ['Laptop', 'Mouse'];,List<String>? hadiahPromo = ['Tas Laptop', 'Stiker'];, danbool beliGaransiTambahan = true;. Buatlah satuList<String> keranjangAkhiryang menggabungkanbarangInti,hadiahPromo(secara aman dengan...?), menambahkan'Kartu Garansi Plus'jikabeliGaransiTambahanbernilaitrue, dan selalu diakhiri dengan'Nota Pembelian'.
Kunci jawaban dan pembahasan
Latihan 1
void main() {
List<String> pendaftarMentah = [
'andi@mail.com',
'budi@mail.com',
'andi@mail.com',
'citra@mail.com',
'budi@mail.com',
];
// Mengubah List menjadi Set untuk membuang email kembar secara otomatis
Set<String> pesertaUnik = pendaftarMentah.toSet();
pesertaUnik.add('dewi@mail.com');
print('Jumlah Pendaftar Awal : ${pendaftarMentah.length} entri');
print('Jumlah Peserta Unik : ${pesertaUnik.length} orang');
print('Daftar Email Unik : $pesertaUnik');
}
// Output:
// Jumlah Pendaftar Awal : 5 entri
// Jumlah Peserta Unik : 4 orang
// Daftar Email Unik : {andi@mail.com, budi@mail.com, citra@mail.com, dewi@mail.com}
Pembahasan: Method .toSet() menyaring seluruh elemen List sehingga 'andi@mail.com' dan 'budi@mail.com' yang muncul dua kali hanya disimpan satu kali. Setelah ditambah 'dewi@mail.com', total peserta unik menjadi 4 orang.
Latihan 2
void main() {
Map<String, int> daftarMenu = {
'Nasi Goreng': 18000,
'Mie Ayam': 15000,
'Es Jeruk': 6000,
};
List<String> pesananPelanggan = ['Nasi Goreng', 'Es Jeruk', 'Kopi Hitam'];
int totalTagihan = 0;
for (final namaMenu in pesananPelanggan) {
int? harga = daftarMenu[namaMenu];
if (harga != null) {
totalTagihan += harga;
print('[Tersedia] $namaMenu: Rp$harga');
} else {
print('[Habis] Maaf, menu "$namaMenu" tidak tersedia.');
}
}
print('Total Tagihan Kantin: Rp$totalTagihan');
}
// Output:
// [Tersedia] Nasi Goreng: Rp18000
// [Tersedia] Es Jeruk: Rp6000
// [Habis] Maaf, menu "Kopi Hitam" tidak tersedia.
// Total Tagihan Kantin: Rp24000
Pembahasan: Mengakses daftarMenu[namaMenu] mengembalikan int?. Dengan memeriksa if (harga != null), kita memanfaatkan type promotion Dart untuk menjumlahkan harga menu yang tersedia dan memberikan pesan informatif ketika pelanggan memesan 'Kopi Hitam' yang tidak terdaftar di Map.
Latihan 3
void main() {
List<String> barangInti = ['Laptop', 'Mouse'];
List<String>? hadiahPromo = ['Tas Laptop', 'Stiker'];
bool beliGaransiTambahan = true;
List<String> keranjangAkhir = [
...barangInti,
...?hadiahPromo,
if (beliGaransiTambahan) 'Kartu Garansi Plus',
'Nota Pembelian',
];
print('Isi Keranjang Akhir (${keranjangAkhir.length} item):');
for (final barang in keranjangAkhir) {
print('- $barang');
}
}
// Output:
// Isi Keranjang Akhir (6 item):
// - Laptop
// - Mouse
// - Tas Laptop
// - Stiker
// - Kartu Garansi Plus
// - Nota Pembelian
Pembahasan: Operator ...barangInti dan ...?hadiahPromo membongkar isi kedua list ke dalam keranjangAkhir, sedangkan if (beliGaransiTambahan) menyisipkan 'Kartu Garansi Plus' secara deklaratif tanpa perlu memanggil .add() di luar deklarasi list.
Diskusi (0)
Kami menyimpan nama (opsional), komentar, dan hash teknis (bukan alamat IP asli) untuk mencegah spam. Cookie fungsional dipakai agar Anda dapat mengedit atau menghapus komentar sendiri dari browser ini. Untuk permintaan penghapusan, gunakan halaman kontak.
Belum ada komentar. Mulai diskusinya.