Collections

Collections #

Hampir tidak ada kode Kotlin yang tidak menyentuh collection. List produk, daftar pengguna, hasil query database, response API — semuanya bermuara ke struktur data yang sama: List, Set, atau Map. Kotlin tidak hanya mewarisi collection dari Java — ia membangunnya ulang dengan API yang jauh lebih ekspresif, sistem immutability yang tegas di level tipe, dan puluhan fungsi transformasi bawaan yang membuat kode terasa seperti mendeskripsikan masalah, bukan memecahkannya langsung. Artikel ini membahas seluruh ekosistem Kotlin Collections: dari perbedaan mutable vs immutable, operasi inti yang paling sering dipakai, hingga komposisi fungsi untuk memproses data kompleks.

Hierarki dan Tipe Collection #

Kotlin memisahkan collection yang bisa diubah (mutable) dan yang tidak bisa diubah (immutable) langsung di level tipe — bukan hanya konvensi atau wrapper seperti Collections.unmodifiableList() di Java. Ini bukan sekadar fitur; ini keputusan desain yang mencegah seluruh kelas bug secara struktural.

flowchart TD
    Iterable["Iterable<T>"] --> Collection["Collection<T>"]
    Collection --> List["List<T>\n(read-only)"]
    Collection --> Set["Set<T>\n(read-only)"]
    Collection --> MutableCollection["MutableCollection<T>"]
    MutableCollection --> MutableList["MutableList<T>"]
    MutableCollection --> MutableSet["MutableSet<T>"]
    Map["Map<K,V>\n(read-only)"] --> MutableMap["MutableMap<K,V>"]

Tiga tipe utama collection di Kotlin:

TipeDeskripsiImplementasi Default
List<T>Urutan elemen, indeks berbasis 0, boleh duplikatArrayList
Set<T>Koleksi unik, tanpa duplikat, tanpa urutan tertentuLinkedHashSet
Map<K, V>Pasangan key-value, key harus unikLinkedHashMap
// Read-only: tidak bisa ditambah, dihapus, atau diubah
val angka: List<Int> = listOf(1, 2, 3, 4, 5)
val kota: Set<String> = setOf("Jakarta", "Bandung", "Surabaya")
val kode: Map<String, Int> = mapOf("IDN" to 62, "MYS" to 60, "SGP" to 65)

// Mutable: bisa dimodifikasi setelah dibuat
val angkaMutable: MutableList<Int> = mutableListOf(1, 2, 3)
val kotaMutable: MutableSet<String> = mutableSetOf("Jakarta", "Bandung")
val kodeMutable: MutableMap<String, Int> = mutableMapOf("IDN" to 62)

// ANTI-PATTERN: selalu pakai MutableList tanpa alasan
// ✗ val produk: MutableList<Produk> = mutableListOf(...)

// BENAR: default ke read-only, mutable hanya saat perlu dimodifikasi
// ✓ val produk: List<Produk> = listOf(...)
listOf(), setOf(), dan mapOf() mengembalikan implementasi read-only. Bukan berarti isinya tidak bisa berubah jika elemen di dalamnya adalah objek mutable — artinya referensi ke elemen di dalam collection tidak bisa ditambah atau dihapus.

Membuat Collection #

Kotlin menyediakan beberapa cara membuat collection, masing-masing cocok untuk skenario berbeda.

Constructor Functions #

// List kosong
val kosong: List<String> = emptyList()

// List dengan satu elemen
val tunggal: List<Int> = listOf(42)

// List dengan elemen awal
val buah = listOf("apel", "jeruk", "mangga", "durian")

// List dengan ukuran dan generator — sangat berguna untuk data dummy
val kuadrat = List(5) { i -> i * i }          // [0, 1, 4, 9, 16]
val matriks = List(3) { baris -> List(3) { kolom -> baris * 3 + kolom } }

// Set
val prioritas = setOf("HIGH", "MEDIUM", "LOW")

// Map
val negara = mapOf(
    "ID" to "Indonesia",
    "MY" to "Malaysia",
    "SG" to "Singapura"
)

buildList, buildSet, buildMap #

buildList, buildSet, dan buildMap adalah cara idiomatik Kotlin untuk membangun collection dengan logika kondisional, tanpa perlu membuat mutable collection lalu mengkonversinya.

// ANTI-PATTERN: buat mutable, isi, lalu assign ke val
val menu = mutableListOf<String>()
menu.add("Nasi Goreng")
menu.add("Mie Ayam")
if (isWeekend) menu.add("Sate")
val menuFinal: List<String> = menu  // konversi manual

// BENAR: gunakan buildList untuk logika kondisional
val menuFinal = buildList {
    add("Nasi Goreng")
    add("Mie Ayam")
    if (isWeekend) add("Sate")
}

// buildMap untuk Map dengan logika
val config = buildMap {
    put("timeout", 30)
    put("retries", 3)
    if (isProduction) {
        put("cache_ttl", 3600)
        put("log_level", "ERROR")
    } else {
        put("log_level", "DEBUG")
    }
}

Operasi Transformasi #

Ini adalah inti dari expressiveness Kotlin Collections. Daripada menulis loop manual, kamu mendeskripsikan transformasi apa yang ingin dilakukan — dan Kotlin mengurusnya.

map — Transformasi Setiap Elemen #

map mengambil setiap elemen, menerapkan fungsi, dan menghasilkan List baru dengan hasil transformasi.

data class Produk(val nama: String, val harga: Double, val stok: Int)

val produk = listOf(
    Produk("Laptop", 15_000_000.0, 10),
    Produk("Mouse", 250_000.0, 50),
    Produk("Keyboard", 800_000.0, 30)
)

// Ambil hanya nama
val namaProduk: List<String> = produk.map { it.nama }
// ["Laptop", "Mouse", "Keyboard"]

// Hitung harga setelah diskon 10%
val hargaDiskon: List<Double> = produk.map { it.harga * 0.9 }
// [13_500_000.0, 225_000.0, 720_000.0]

// Transformasi ke tipe lain
data class ProdukRingkas(val nama: String, val hargaFormatted: String)

val ringkasan = produk.map { p ->
    ProdukRingkas(p.nama, "Rp ${"%,.0f".format(p.harga)}")
}

filter — Saring Berdasarkan Kondisi #

filter menghasilkan List baru yang hanya berisi elemen yang memenuhi predikat.

// Produk dengan stok > 20
val tersedia = produk.filter { it.stok > 20 }

// Produk mahal (> 1 juta)
val mahal = produk.filter { it.harga > 1_000_000.0 }

// filterNot: kebalikan filter
val murah = produk.filterNot { it.harga > 1_000_000.0 }

// filterIsInstance: filter berdasarkan tipe
val campuran: List<Any> = listOf(1, "dua", 3.0, "empat", 5)
val hanyaString: List<String> = campuran.filterIsInstance<String>()
// ["dua", "empat"]

// filterNotNull: hapus elemen null dari nullable list
val denganNull: List<String?> = listOf("apel", null, "jeruk", null, "mangga")
val bersih: List<String> = denganNull.filterNotNull()
// ["apel", "jeruk", "mangga"]

flatMap — Ratakan Collection Bersarang #

flatMap berguna saat setiap elemen menghasilkan list, dan kamu ingin semua hasilnya dalam satu list flat.

data class Kategori(val nama: String, val produk: List<String>)

val katalog = listOf(
    Kategori("Elektronik", listOf("Laptop", "HP", "Tablet")),
    Kategori("Aksesoris", listOf("Mouse", "Keyboard", "Headset")),
    Kategori("Peripherals", listOf("Monitor", "Webcam"))
)

// ANTI-PATTERN: loop manual + addAll
val semuaProduk = mutableListOf<String>()
for (kategori in katalog) {
    semuaProduk.addAll(kategori.produk)
}

// BENAR: flatMap
val semuaProduk = katalog.flatMap { it.produk }
// ["Laptop", "HP", "Tablet", "Mouse", "Keyboard", "Headset", "Monitor", "Webcam"]

// flatten: jika sudah punya List<List<T>>
val matriks = listOf(listOf(1, 2, 3), listOf(4, 5, 6), listOf(7, 8, 9))
val flat = matriks.flatten()
// [1, 2, 3, 4, 5, 6, 7, 8, 9]

Operasi Pengelompokan #

groupBy — Kelompokkan Berdasarkan Kunci #

groupBy menghasilkan Map<K, List<V>> di mana setiap key adalah hasil dari fungsi pengelompokan.

data class Transaksi(
    val id: Int,
    val kategori: String,
    val nominal: Double,
    val tanggal: String
)

val transaksi = listOf(
    Transaksi(1, "Makanan", 85_000.0, "2024-01"),
    Transaksi(2, "Transport", 45_000.0, "2024-01"),
    Transaksi(3, "Makanan", 120_000.0, "2024-02"),
    Transaksi(4, "Hiburan", 200_000.0, "2024-02"),
    Transaksi(5, "Transport", 35_000.0, "2024-02")
)

// Kelompokkan berdasarkan kategori
val perKategori: Map<String, List<Transaksi>> = transaksi.groupBy { it.kategori }
// {
//   "Makanan"   -> [Transaksi(1,...), Transaksi(3,...)],
//   "Transport" -> [Transaksi(2,...), Transaksi(5,...)],
//   "Hiburan"   -> [Transaksi(4,...)]
// }

// groupBy dengan value transform: langsung ambil nominal, bukan objek penuh
val nominalPerKategori: Map<String, List<Double>> =
    transaksi.groupBy({ it.kategori }, { it.nominal })
// {"Makanan" -> [85000.0, 120000.0], "Transport" -> [45000.0, 35000.0], ...}

partition — Pisah Jadi Dua Kelompok #

partition memecah collection menjadi dua list berdasarkan predikat — elemen yang memenuhi dan yang tidak.

// ANTI-PATTERN: dua kali filter untuk hal yang berlawanan
val stokAda = produk.filter { it.stok > 0 }
val habis = produk.filter { it.stok == 0 }

// BENAR: partition — satu kali iterasi, dua hasil
val (stokAda, habis) = produk.partition { it.stok > 0 }

// Contoh nyata: pisahkan transaksi valid dan invalid
val (valid, invalid) = transaksi.partition { it.nominal > 0 }

associateBy — Konversi List ke Map #

associateBy berguna untuk mengubah List menjadi Map untuk lookup cepat.

// ANTI-PATTERN: loop manual untuk bangun Map dari List
val produkById = mutableMapOf<Int, Produk>()
for (p in produk) {
    produkById[p.id] = p
}

// BENAR: associateBy
data class ProdukLengkap(val id: Int, val nama: String, val harga: Double)

val daftarProduk = listOf(
    ProdukLengkap(1, "Laptop", 15_000_000.0),
    ProdukLengkap(2, "Mouse", 250_000.0),
    ProdukLengkap(3, "Keyboard", 800_000.0)
)

val produkById: Map<Int, ProdukLengkap> = daftarProduk.associateBy { it.id }
val laptop = produkById[1]  // lookup O(1), bukan O(n)

// associate: kontrol penuh atas key dan value
val namaById: Map<Int, String> = daftarProduk.associate { it.id to it.nama }
// {1 -> "Laptop", 2 -> "Mouse", 3 -> "Keyboard"}

Operasi Agregasi #

Operasi agregasi merangkum seluruh collection menjadi satu nilai.

sum, count, min, max #

val nilai = listOf(85, 92, 78, 95, 88, 71, 90)

val total = nilai.sum()                        // 599
val rata = nilai.average()                     // 85.57...
val terbesar = nilai.max()                     // 95
val terkecil = nilai.min()                     // 71
val jumlah = nilai.count()                     // 7
val lulusan = nilai.count { it >= 80 }         // 5

// sumOf, minOf, maxOf — untuk property tertentu
val totalHarga = produk.sumOf { it.harga }
val hargaTermurah = produk.minOf { it.harga }
val hargaTermahal = produk.maxOf { it.harga }

// minByOrNull, maxByOrNull — mengembalikan elemen, bukan nilainya
val produkTermurah = produk.minByOrNull { it.harga }   // Produk, bukan Double
val produkTermahal = produk.maxByOrNull { it.harga }

reduce dan fold #

reduce dan fold menggabungkan semua elemen menjadi satu nilai melalui operasi akumulasi. Perbedaannya: fold punya nilai awal, reduce tidak.

val angka = listOf(1, 2, 3, 4, 5)

// reduce: elemen pertama jadi accumulator awal
val jumlah = angka.reduce { acc, angka -> acc + angka }  // 15
val perkalian = angka.reduce { acc, n -> acc * n }       // 120

// fold: kamu tentukan nilai awal
val jumlahDenganBonus = angka.fold(100) { acc, n -> acc + n }  // 115

// Contoh praktis: bangun string dari list
val kata = listOf("Kotlin", "adalah", "bahasa", "yang", "elegan")
val kalimat = kata.fold("") { acc, w -> if (acc.isEmpty()) w else "$acc $w" }
// "Kotlin adalah bahasa yang elegan"

// Versi lebih idiomatik: joinToString
val kalimatIdiomatic = kata.joinToString(" ")

any, all, none #

val nilai = listOf(85, 92, 78, 95, 88)

val adaYangLulus = nilai.any { it >= 80 }      // true
val semuaLulus = nilai.all { it >= 80 }         // false (78 tidak memenuhi)
val tidakAdaGagal = nilai.none { it < 60 }      // true

// Contoh nyata: validasi list pesanan
data class ItemPesanan(val nama: String, val stok: Int, val harga: Double)

val pesanan = listOf(
    ItemPesanan("Laptop", 3, 15_000_000.0),
    ItemPesanan("Mouse", 0, 250_000.0),   // stok habis!
    ItemPesanan("Keyboard", 5, 800_000.0)
)

val bisakirimSemua = pesanan.all { it.stok > 0 }       // false
val adaStokHabis = pesanan.any { it.stok == 0 }        // true
val semuaAdaStok = pesanan.none { it.stok == 0 }       // false

Operasi Pengurutan #

sort dan sortedBy #

val nama = mutableListOf("Zara", "Andi", "Budi", "Clara")

// sort: in-place, hanya untuk MutableList
nama.sort()              // ["Andi", "Budi", "Clara", "Zara"]
nama.sortDescending()    // ["Zara", "Clara", "Budi", "Andi"]

// sorted: menghasilkan List baru, aman untuk read-only
val namaTerurut = listOf("Zara", "Andi", "Budi").sorted()

// sortedBy: urutkan berdasarkan property
val produkUrut = produk.sortedBy { it.harga }             // harga terendah dulu
val produkUrutTurun = produk.sortedByDescending { it.harga }

// sortedWith: comparator kompleks — urutkan berdasarkan beberapa kriteria
val produkKompleks = produk.sortedWith(
    compareBy({ it.kategori }, { it.harga })  // kategori dulu, lalu harga
)

// compareByDescending untuk kombinasi arah berbeda
val produkMixed = produk.sortedWith(
    compareByDescending<Produk> { it.stok }.thenBy { it.harga }
    // stok tinggi dulu, jika sama urutkan harga terendah
)

Operasi pada Map #

Map memiliki API tersendiri yang worth diketahui.

val inventaris = mapOf(
    "Laptop" to 10,
    "Mouse" to 50,
    "Keyboard" to 30,
    "Monitor" to 8
)

// Akses nilai — gunakan getOrDefault untuk menghindari null
val stokLaptop = inventaris["Laptop"]              // Int? (nullable)
val stokWebcam = inventaris.getOrDefault("Webcam", 0)  // 0 — aman
val stokTablet = inventaris.getOrElse("Tablet") { 0 }  // sama, tapi dengan lambda

// Filter pada Map
val stokRendah = inventaris.filter { (_, stok) -> stok < 20 }
// {"Laptop" -> 10, "Monitor" -> 8}

// Transformasi value
val stokKritisFlag = inventaris.mapValues { (_, stok) -> stok < 15 }
// {"Laptop" -> true, "Mouse" -> false, ...}

// Transformasi key
val inventarisUpper = inventaris.mapKeys { (nama, _) -> nama.uppercase() }

// any, all pada Map
val adaStokKritis = inventaris.any { (_, stok) -> stok < 10 }  // true (Monitor = 8)
val semuaCukup = inventaris.all { (_, stok) -> stok >= 5 }      // true

Komposisi Operasi #

Kekuatan sejati Kotlin Collections muncul saat kamu merantai beberapa operasi. Setiap operasi menghasilkan collection baru yang bisa langsung dilanjutkan dengan operasi berikutnya.

data class Karyawan(
    val nama: String,
    val departemen: String,
    val gaji: Double,
    val aktif: Boolean
)

val karyawan = listOf(
    Karyawan("Andi", "Engineering", 15_000_000.0, true),
    Karyawan("Budi", "Marketing", 12_000_000.0, true),
    Karyawan("Clara", "Engineering", 18_000_000.0, true),
    Karyawan("Dedi", "HR", 10_000_000.0, false),
    Karyawan("Eva", "Engineering", 20_000_000.0, true),
    Karyawan("Fani", "Marketing", 13_500_000.0, true)
)

// Kasus 1: Total gaji karyawan Engineering yang aktif
val totalGajiEngineering = karyawan
    .filter { it.aktif && it.departemen == "Engineering" }
    .sumOf { it.gaji }
// 53_000_000.0

// Kasus 2: Nama karyawan aktif, diurutkan, digabung jadi string
val daftarAktif = karyawan
    .filter { it.aktif }
    .sortedBy { it.nama }
    .map { it.nama }
    .joinToString(", ")
// "Andi, Budi, Clara, Eva, Fani"

// Kasus 3: Rata-rata gaji per departemen (hanya yang aktif)
val rataGajiPerDept = karyawan
    .filter { it.aktif }
    .groupBy { it.departemen }
    .mapValues { (_, karyawanDept) ->
        karyawanDept.map { it.gaji }.average()
    }
// {"Engineering" -> 17_666_666.67, "Marketing" -> 12_750_000.0}

// Kasus 4: Departemen dengan total gaji tertinggi
val deptTertinggi = karyawan
    .filter { it.aktif }
    .groupBy { it.departemen }
    .mapValues { (_, list) -> list.sumOf { it.gaji } }
    .maxByOrNull { it.value }
    ?.key
// "Engineering"
flowchart LR
    A["karyawan\n(List&lt;Karyawan&gt;)"] --> B["filter { aktif }"]
    B --> C["groupBy { departemen }"]
    C --> D["mapValues { average() }"]
    D --> E["Map&lt;String, Double&gt;\nrata-rata gaji/dept"]

Operasi Tambahan yang Sering Dipakai #

take, drop, slice #

val angka = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

val tiga = angka.take(3)           // [1, 2, 3]
val tigaAkhir = angka.takeLast(3)  // [8, 9, 10]
val buang3 = angka.drop(3)         // [4, 5, 6, 7, 8, 9, 10]
val slice = angka.slice(2..5)      // [3, 4, 5, 6]

// takeWhile / dropWhile: berhenti saat kondisi tidak terpenuhi
val kecilDari5 = angka.takeWhile { it < 5 }   // [1, 2, 3, 4]
val ab5Keatas = angka.dropWhile { it < 5 }    // [5, 6, 7, 8, 9, 10]

distinct dan zip #

// distinct: hapus duplikat
val denganDuplikat = listOf(1, 2, 2, 3, 3, 3, 4)
val unik = denganDuplikat.distinct()    // [1, 2, 3, 4]

// distinctBy: unik berdasarkan property
val produkDuplikat = listOf(
    Produk("Laptop", 15_000_000.0, 10),
    Produk("Laptop", 14_000_000.0, 5),   // nama sama
    Produk("Mouse", 250_000.0, 50)
)
val produkUnik = produkDuplikat.distinctBy { it.nama }
// [Produk("Laptop",...), Produk("Mouse",...)] — Laptop kedua dibuang

// zip: gabungkan dua list menjadi list of Pair
val kunci = listOf("nama", "kota", "negara")
val nilai = listOf("Andi", "Jakarta", "Indonesia")
val gabung = kunci.zip(nilai)
// [("nama", "Andi"), ("kota", "Jakarta"), ("negara", "Indonesia")]

// zip dengan transform langsung
val map = kunci.zip(nilai) { k, v -> k to v }.toMap()
// {"nama" -> "Andi", "kota" -> "Jakarta", ...}

// unzip: kebalikan zip
val (keys, values) = gabung.unzip()

chunked dan windowed #

val data = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9)

// chunked: pecah jadi batch dengan ukuran tetap
val batch = data.chunked(3)
// [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

// Berguna untuk pagination atau batch processing
val halaman = daftarProduk.chunked(10)  // 10 produk per halaman

// windowed: sliding window
val jendela = data.windowed(3)
// [[1,2,3], [2,3,4], [3,4,5], [4,5,6], [5,6,7], [6,7,8], [7,8,9]]

// windowed dengan step
val jendelaStep = data.windowed(3, step = 2)
// [[1,2,3], [3,4,5], [5,6,7], [7,8,9]]
Setiap operasi transformasi seperti map, filter, dan groupBy menghasilkan collection baru. Untuk collection yang sangat besar (ratusan ribu elemen) dengan banyak operasi berantai, pertimbangkan menggunakan Sequence agar evaluasi bersifat lazy dan tidak membuat collection perantara yang tidak perlu.

Kapan Menggunakan List, Set, atau Map #

Pilihan tipe collection bukan sekadar preferensi — ini memengaruhi semantik dan performa kode.

flowchart TD
    A{Butuh pasangan\nkey-value?} -- Ya --> B["Map&lt;K, V&gt;\nlookup O(1)"]
    A -- Tidak --> C{Urutan penting?}
    C -- Ya --> D["List&lt;T&gt;\nurutan terjaga, akses by index"]
    C -- Tidak --> E{Boleh duplikat?}
    E -- Ya --> D
    E -- Tidak --> F["Set&lt;T&gt;\nunik, operasi himpunan"]
Gunakan List jika:
  ✓ Urutan elemen penting (data diproses berurutan)
  ✓ Butuh akses by index (produk[0], produk[5])
  ✓ Boleh ada duplikat (daftar log, histori transaksi)
  ✓ Sebagian besar use case harian

Gunakan Set jika:
  ✓ Keunikan elemen penting (tag, permission, kategori)
  ✓ Butuh operasi himpunan: intersect, union, subtract
  ✓ Sering cek apakah elemen ada: contains() — O(1) vs O(n) pada List

Gunakan Map jika:
  ✓ Butuh lookup cepat berdasarkan kunci
  ✓ Data berpasangan: ID → Objek, kode → nilai
  ✓ Pengelompokan hasil groupBy
// Set lebih tepat untuk membership check yang sering dilakukan
val rolesDiizinkan: Set<String> = setOf("ADMIN", "EDITOR", "MODERATOR")
fun bolehAkses(role: String) = role in rolesDiizinkan  // O(1)

// Map untuk cache / lookup table
val kodeNegara: Map<String, String> = mapOf("ID" to "Indonesia", "MY" to "Malaysia")

// Operasi himpunan pada Set
val aSet = setOf(1, 2, 3, 4, 5)
val bSet = setOf(3, 4, 5, 6, 7)

val irisan = aSet intersect bSet     // {3, 4, 5}
val gabungan = aSet union bSet       // {1, 2, 3, 4, 5, 6, 7}
val selisih = aSet subtract bSet     // {1, 2}

Ringkasan #

  • Immutable by default — selalu gunakan listOf, setOf, mapOf kecuali memang perlu modifikasi. MutableList hanya saat kamu benar-benar butuh add/remove.
  • map mengubah setiap elemen ke bentuk lain, filter menyaring berdasarkan kondisi, flatMap meratakan list bersarang — tiga operasi ini menyelesaikan 80% kebutuhan transformasi.
  • groupBy menghasilkan Map<K, List<V>> yang sangat berguna untuk agregasi data per kategori, departemen, atau status.
  • partition memisahkan collection menjadi dua kelompok dalam satu iterasi — lebih efisien dari dua kali filter.
  • associateBy mengubah List<T> menjadi Map<K, T> untuk lookup cepat O(1) — hindari loop manual untuk membangun Map dari List.
  • fold dan reduce untuk akumulasi; any, all, none untuk kondisi; sumOf, maxByOrNull, minByOrNull untuk agregasi per property.
  • Komposisi operasi adalah kekuatan utama — filter, map, groupBy, sortedBy bisa dirantai untuk memproses data kompleks tanpa loop manual.
  • Pilih tipe yang tepat: List untuk urutan, Set untuk keunikan dan operasi himpunan, Map untuk pasangan key-value dan lookup cepat.
  • buildList / buildMap adalah cara idiomatik untuk membangun collection dengan logika kondisional tanpa perlu membuat mutable lalu mengkonversinya.
  • Untuk collection sangat besar dengan banyak operasi berantai, pertimbangkan Sequence agar tidak membuat collection perantara yang tidak perlu.

← Sebelumnya: Math   Berikutnya: Scope Functions →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact