Array dan List sering dipakai bergantian untuk menyimpan sekumpulan nilai, tetapi keduanya punya karakteristik yang berbeda. Array berukuran tetap dengan elemen yang bisa diganti; List dapat bersifat read-only atau mutable. Pada chapter ini kita akan membandingkan keduanya secara langsung, mulai dari kemampuan mutasi, operasi yang tersedia, hingga rekomendasi kapan memilih satu di antaranya.
Tabel Perbandingan #
Berikut ringkasan perbedaan utama:
| Aspek | Array<T> | List<T> | MutableList<T> |
|---|---|---|---|
| Ukuran | Tetap, tidak bisa berubah | Tetap (read-only) | Bisa berubah |
| Elemen | Bisa diganti melalui [i] = x | Tidak bisa diganti | Bisa diganti |
add / remove | Tidak ada | Tidak ada | Ada |
| Operasi stdlib | Dasar | Sangat banyak | Sangat banyak |
| Tipe elemen | Selalu objek (boxed) | Bebas (atau primitif via IntArray) | Bebas |
| Interop Java | T[] | java.util.List | java.util.List |
| Konstruktor default | arrayOf, Array(n) { ... } | listOf, emptyList() | mutableListOf |
Karakteristik Mutasi #
Array hanya mengizinkan penggantian nilai pada indeks yang sudah ada — ukuran tidak bisa berubah. MutableList mengizinkan penambahan dan penghapusan elemen sehingga ukuran bisa tumbuh atau menyusut.
val arr = arrayOf(1, 2, 3)
arr[0] = 100 // diperbolehkan
// arr.add(4) // tidak ada metode add
val mlist = mutableListOf(1, 2, 3)
mlist[0] = 100 // diperbolehkan
mlist.add(4) // diperbolehkan: ukuran menjadi 4List<T> (read-only) berdiri di tengah: ia tidak mengizinkan penggantian elemen maupun perubahan ukuran melalui API publik. Karena itu, List adalah pilihan terbaik untuk parameter fungsi dan properti publik yang tidak boleh dimutasi oleh pemanggil.
Operasi yang Tersedia #
Keduanya mendukung iterasi dan operasi fungsional, tetapi List memiliki lebih banyak operasi bawaan pustaka standar: map, filter, fold, sorted, chunked, windowed, groupBy, dan ratusan lainnya. Array<T> mendukung sebagian besar operasi yang sama melalui extension function, tetapi selalu mengembalikan List<T>:
val arr = arrayOf(1, 2, 3)
val hasil = arr.map { it * 2 } // List<Int>, bukan Array<Int>
val filtered = arr.filter { it > 1 } // List<Int>Pada List, hasil operasi tetap List, sehingga komposisi operasi tetap dalam ranah yang sama.
Contoh Lengkap #
fun main() {
// === Perbandingan ukuran ===
val array = arrayOf(1, 2, 3)
val list = listOf(1, 2, 3)
val mutableList = mutableListOf(1, 2, 3)
println("Array size: ${array.size}, List size: ${list.size}")
// === Akses indeks (sama) ===
println("array[0]: ${array[0]}, list[0]: ${list[0]}")
// === Mutasi elemen ===
array[0] = 100
mutableList[0] = 100
// list[0] = 100 // compile error: List read-only
println("Array setelah modifikasi: ${array.toList()}")
println("MutableList setelah modifikasi: $mutableList")
// === Ukuran bisa berubah hanya pada MutableList ===
mutableList.add(4)
println("MutableList setelah add: $mutableList")
// array.add(4) // tidak ada metode add
// list.add(4) // tidak bisa
// === Operasi bawaan ===
println("List.map: ${list.map { it * 2 }}")
println("List.filter: ${list.filter { it > 1 }}")
println("Array.map: ${array.map { it * 2 }}")
println("Array.toList(): ${array.toList()}")
// === Konversi dua arah ===
val arrayKeList: List<Int> = array.toList()
val listKeArray: Array<Int> = list.toTypedArray()
println("Array -> List: $arrayKeList")
println("List -> Array: ${listKeArray.toList()}")
// === Interop dengan varargs Java ===
fun jumlah(vararg angka: Int): Int = angka.sum()
val denganArray = jumlah(*array.toIntArray()) // spread operator
println("Hasil varargs: $denganArray")
// === Iterasi identik ===
for (x in array) print("$x ")
println()
for (x in list) print("$x ")
println()
// === Perbedaan tipe runtime ===
println("Runtime type array: ${array::class.simpleName}")
println("Runtime type list: ${list::class.simpleName}")
println("Runtime type mutableList: ${mutableList::class.simpleName}")
// === Perbandingan fitur (tabel ringkas dicetak) ===
println("--- Perbandingan ---")
println("Array | size tetap | elemen mutable | add/remove: tidak")
println("List | size tetap | read-only | add/remove: tidak")
println("MutableList | size ubah | elemen mutable | add/remove: ya")
}Output yang Dihasilkan:
Array size: 3, List size: 3
array[0]: 1, list[0]: 1
Array setelah modifikasi: [100, 2, 3]
MutableList setelah modifikasi: [100, 2, 3]
MutableList setelah add: [100, 2, 3, 4]
List.map: [2, 4, 6]
List.filter: [2, 3]
Array.map: [200, 4, 6]
Array.toList(): [100, 2, 3]
Array -> List: [100, 2, 3]
List -> Array: [1, 2, 3]
Hasil varargs: 105
100 2 3
1 2 3
Runtime type array: Array
Runtime type list: ArrayList
Runtime type mutableList: ArrayList
--- Perbandingan ---
Array | size tetap | elemen mutable | add/remove: tidak
List | size tetap | read-only | add/remove: tidak
MutableList | size ubah | elemen mutable | add/remove: yaMari kita bedah baris-baris penting di atas:
array[0] = 100: penggantian elemenArraybekerja karena elemen bersifat mutable.list[0] = 100padaListakan ditolak kompilator.mutableList.add(4): menambah elemen di akhir, mengubahsizedari 3 menjadi 4. Tidak ada padanan padaArray.array.map { it * 2 }: mengembalikanList<Int>, bukanArray<Int>. Untuk mendapatkan kembaliArray, kita perlu memanggil.toTypedArray().array.toList()danlist.toTypedArray(): konversi dua arah. Konversi ini membuat salinan, bukan view, sehingga perubahan pada satu tidak memengaruhi yang lain.jumlah(*array.toIntArray()): spread operator*membongkar array menjadi argumen varargs. UntukArray<Int>, kita harus mengonversi keIntArraylebih dulu karena signaturevararg angka: Intdi JVM memerlukanint[].
Kapan Memilih Array atau List #
Rekomendasi praktis:
- Default pilih
List. API-nya kaya, tipe eksplisit (read-only vs mutable), dan tidak perlu pusing dengan boxing. - Pilih
Array<T>ketika memanggil API Java yang menerimaT[]atauvarargs. - Pilih array primitif (
IntArray,LongArray, dll.) untuk koleksi besar atau hot path. - Pilih
MutableListketika ukuran perlu berubah seiring waktu dan kita ingin tetap menggunakan APIListdi tempat lain.
Referensi Lanjutan #
- Collections overview
— perbandingan
Array<T>danList<T> - List API reference
— API reference
kotlin.collections.List - ArrayList API reference
— API reference
kotlin.collections.ArrayList
Setelah memahami perbedaan struktur dasar collection, kita siap masuk ke ranah operasi fungsional yang menjadi kekuatan utama Kotlin: map, filter, forEach, dan kawan-kawannya akan dibahas pada chapter operasi collection.