Hampir semua aplikasi nyata bekerja dengan kumpulan data: daftar produk, riwayat transaksi, atau mapping kode ke nama. C# menyediakan berbagai koleksi yang powerful dan type-safe melalui System.Collections.Generic. Di artikel ini kita akan mempelajari koleksi yang paling sering digunakan beserta pengenalan LINQ — cara elegan untuk memfilter dan mentransformasi data.
Array vs List
// Array: ukuran tetap, performa sedikit lebih baik
int[] nilai_array = { 85, 92, 78, 95, 88 };
nilai_array[0] = 90; // ✅ Ubah nilai
// nilai_array.Length = 10; // ❌ Ukuran tidak bisa diubah
// List<T>: ukuran dinamis, lebih fleksibel
List<int> nilai_list = new List<int> { 85, 92, 78, 95, 88 };
nilai_list.Add(91); // Tambah elemen
nilai_list.Remove(78); // Hapus nilai tertentu
nilai_list.RemoveAt(0); // Hapus di indeks tertentu
Console.WriteLine(nilai_list.Count); // Jumlah elemen
// Output: 5
List — Koleksi Dinamis
List<string> kota = new List<string>();
// Menambahkan elemen
kota.Add("Jakarta");
kota.Add("Surabaya");
kota.Add("Bandung");
kota.AddRange(new[] { "Medan", "Makassar" }); // Tambah banyak sekaligus
// Akses dan iterasi
Console.WriteLine(kota[0]); // Output: Jakarta
Console.WriteLine(kota.Count); // Output: 5
foreach (string k in kota)
{
Console.WriteLine($"- {k}");
}
// Pencarian
bool ada_bandung = kota.Contains("Bandung");
int indeks = kota.IndexOf("Surabaya");
Console.WriteLine($"Ada Bandung: {ada_bandung}, Indeks Surabaya: {indeks}");
// Output: Ada Bandung: True, Indeks Surabaya: 1
// Urutkan
kota.Sort();
Console.WriteLine(string.Join(", ", kota));
// Output: Bandung, Jakarta, Makassar, Medan, Surabaya
Collection Initializer dan var
// Cara modern — lebih ringkas
var mahasiswa = new List<string> { "Ali", "Budi", "Candra" };
// Target-typed new expression (C# 9+)
List<int> angka = new() { 1, 2, 3, 4, 5 };
Dictionary<TKey, TValue> — Pasangan Key-Value
var populasi = new Dictionary<string, long>
{
{ "Jakarta", 10_560_000 },
{ "Surabaya", 2_874_000 },
{ "Bandung", 2_444_000 }
};
// Akses nilai
Console.WriteLine(populasi["Jakarta"]);
// Output: 10560000
// Tambah atau update elemen
populasi["Medan"] = 2_435_000;
populasi["Jakarta"] = 10_600_000; // Update existing
// Pengecekan yang aman
if (populasi.TryGetValue("Bali", out long pop_bali))
{
Console.WriteLine($"Populasi Bali: {pop_bali}");
}
else
{
Console.WriteLine("Data Bali tidak ditemukan");
}
// Output: Data Bali tidak ditemukan
// Iterasi
foreach (var (kota, pop) in populasi)
{
Console.WriteLine($"{kota}: {pop:N0} jiwa");
}
Gunakan
TryGetValue()daripadadict[key]langsung — akses langsung melemparKeyNotFoundExceptionjika key tidak ada.
Queue dan Stack
// Queue: FIFO (First In, First Out) — seperti antrian kasir
var antrian = new Queue<string>();
antrian.Enqueue("Pelanggan A");
antrian.Enqueue("Pelanggan B");
antrian.Enqueue("Pelanggan C");
Console.WriteLine($"Sedang dilayani: {antrian.Dequeue()}");
// Output: Sedang dilayani: Pelanggan A
Console.WriteLine($"Berikutnya: {antrian.Peek()}");
// Output: Berikutnya: Pelanggan B (tidak dihapus)
// Stack: LIFO (Last In, First Out) — seperti tumpukan piring
var riwayat = new Stack<string>();
riwayat.Push("Buka halaman A");
riwayat.Push("Buka halaman B");
riwayat.Push("Buka halaman C");
Console.WriteLine($"Undo: {riwayat.Pop()}");
// Output: Undo: Buka halaman C
Console.WriteLine($"Halaman saat ini: {riwayat.Peek()}");
// Output: Halaman saat ini: Buka halaman B
LINQ — Language Integrated Query
LINQ adalah fitur revolusioner C# yang memungkinkan query data secara deklaratif — seperti SQL tapi untuk koleksi C#.
LINQ Method Syntax
var produk = new List<(string nama, int harga, string kategori)>
{
("Laptop", 15_000_000, "Elektronik"),
("Mouse", 250_000, "Elektronik"),
("Baju", 150_000, "Fashion"),
("Sepatu", 500_000, "Fashion"),
("Monitor", 3_500_000, "Elektronik"),
("Tas", 350_000, "Fashion"),
};
// Filter: hanya elektronik
var elektronik = produk.Where(p => p.kategori == "Elektronik");
// Transform: ambil nama saja
var nama_produk = produk.Select(p => p.nama);
// Sort
var termahal = produk.OrderByDescending(p => p.harga);
// Gabungan: elektronik di bawah 1 juta, urutkan
var elektronik_murah = produk
.Where(p => p.kategori == "Elektronik" && p.harga < 1_000_000)
.OrderBy(p => p.harga)
.Select(p => $"{p.nama}: Rp {p.harga:N0}");
foreach (var item in elektronik_murah)
Console.WriteLine(item);
// Output: Mouse: Rp 250.000
LINQ Aggregasi
var nilai = new List<int> { 85, 92, 78, 95, 88, 72, 91 };
Console.WriteLine($"Rata-rata : {nilai.Average():F1}"); // Output: 85.9
Console.WriteLine($"Nilai max : {nilai.Max()}"); // Output: 95
Console.WriteLine($"Nilai min : {nilai.Min()}"); // Output: 72
Console.WriteLine($"Total : {nilai.Sum()}"); // Output: 601
Console.WriteLine($"Lulus (≥80): {nilai.Count(n => n >= 80)}"); // Output: 5
// FirstOrDefault: kembalikan null jika tidak ada (tidak throw exception)
int nilai_pertama_a = nilai.FirstOrDefault(n => n >= 90);
Console.WriteLine(nilai_pertama_a); // Output: 92
LINQ Query Syntax (SQL-like)
var mahasiswa = new List<(string nama, double ipk)>
{
("Ali", 3.75), ("Budi", 3.20), ("Candra", 3.90),
("Dewi", 3.50), ("Eko", 2.80)
};
// Query syntax — mirip SQL
var cum_laude = from m in mahasiswa
where m.ipk >= 3.5
orderby m.ipk descending
select new { m.nama, m.ipk };
foreach (var m in cum_laude)
Console.WriteLine($"{m.nama}: {m.ipk}");
// Output:
// Candra: 3.9
// Ali: 3.75
// Dewi: 3.5
Pertanyaan yang Sering Diajukan
Kapan menggunakan List<T> vs array?
Gunakan List<T> sebagai default untuk koleksi yang ukurannya bisa berubah (add/remove). Gunakan array hanya jika ukuran tetap dan performa sangat kritis, atau saat berinteraksi dengan API yang membutuhkan array. Dalam praktik sehari-hari, List<T> adalah pilihan yang lebih aman dan fleksibel.
Apa itu LINQ dan apakah selalu lebih baik dari loop biasa?
LINQ adalah cara deklaratif untuk bekerja dengan koleksi — kamu menyatakan apa yang kamu inginkan, bukan bagaimana cara mendapatkannya. LINQ lebih mudah dibaca untuk operasi filter/transform sederhana. Tapi untuk operasi kompleks dengan banyak kondisi atau yang butuh optimasi performa, loop biasa bisa lebih mudah dipahami. Gunakan LINQ untuk keterbacaan, loop untuk kontrol penuh.
Mengapa ada TryGetValue untuk Dictionary, bukan akses langsung?
Akses dict["key"] melempar KeyNotFoundException jika key tidak ada — ini adalah exception yang harus ditangani. TryGetValue mengembalikan false dan nilai default jika key tidak ada, tanpa exception. Ini lebih aman dan lebih performant karena tidak ada overhead exception handling. Gunakan TryGetValue saat kamu tidak yakin apakah key ada.
Apa perbedaan First() dan FirstOrDefault()?
First() melempar InvalidOperationException jika koleksi kosong atau tidak ada elemen yang memenuhi kondisi. FirstOrDefault() mengembalikan nilai default tipe (null untuk reference type, 0 untuk int, dll.). Gunakan First() saat kamu yakin elemen pasti ada; FirstOrDefault() saat elemen mungkin tidak ada.
Kesimpulan
| Koleksi | Kapan Digunakan |
|---|---|
List<T> | Koleksi dinamis umum (paling sering dipakai) |
Dictionary<K,V> | Pencarian cepat berdasarkan key |
Queue<T> | Antrian (FIFO): pemrosesan berurutan |
Stack<T> | Tumpukan (LIFO): undo/history |
LINQ .Where() | Filter data |
LINQ .Select() | Transformasi data |
LINQ .OrderBy() | Pengurutan |
Artikel sebelumnya: Fungsi dan Method di C# — cara membuat method di C#.
Langkah selanjutnya: Class dan Object di C# — dasar pemrograman berorientasi objek di C#.