Odin menyediakan tiga bentuk penyimpanan berurutan yang berbeda dalam hal ukuran dan kepemilikan data: array tetap, slice, dan dynamic array, ditambah map untuk data berkunci. Topik ini membahas cara membuat dan mengiterasi keempatnya, semantik penyalinan array dibanding semantik pandangan slice, pertumbuhan kapasitas dynamic array, serta operasi dasar map. Setelah membaca, pemilihan tipe penyimpanan yang tepat untuk kebutuhan tertentu dapat ditentukan beserta alasan tata letaknya.
Array tetap: ukuran terkunci dan penyalinan nilai#
Array tetap menyimpan sejumlah elemen yang diketahui pada saat kompilasi, dengan tipe ditulis [4]int untuk empat bilangan bulat. Ukurannya adalah jumlah elemen dikalikan ukuran elemen, sehingga [4]int menempati 32 byte pada int berukuran 8 byte. Array merupakan nilai penuh, artinya penugasan menyalin seluruh isi array, bukan hanya rujukannya. Perubahan pada salinan tidak menyentuh array aslinya.
package main
import "core:fmt"
main :: proc() {
// array tetap: disalin nilai demi nilai saat ditugaskan
a := [4]int{10, 20, 30, 40}
b := a
b[0] = 99
fmt.println("array asli :", a)
fmt.println("array salinan:", b)
fmt.println("ukuran [4]int =", size_of([4]int), "byte")
// slice: pandangan atas data array yang sudah ada
s := a[1:3]
fmt.println("slice s = a[1:3]:", s)
s[0] = 21
fmt.println("array ikut berubah:", a)
fmt.println("ukuran tipe slice =", size_of([]int), "byte")
// iterasi array: nilai lebih dulu, indeks kemudian
for v, i in a {
fmt.printf("a[%d] = %d\n", i, v)
}
// dynamic array: panjang bertambah dengan append
d := make([dynamic]int, 0, 4)
fmt.println("d awal: len =", len(d), "cap =", cap(d))
for i := 1; i <= 9; i += 1 {
append(&d, i * i)
}
fmt.println("d isi:", d)
fmt.println("d akhir: len =", len(d), "cap =", cap(d))
delete(d)
// map: pasangan kunci dan nilai
stok := map[string]int{}
stok["Pensil"] = 12
stok["Buku"] = 8
stok["Penghapus"] = 5
fmt.println("stok Buku:", stok["Buku"])
fmt.println("len(stok) =", len(stok))
fmt.println("ada kunci Buku?", "Buku" in stok)
if v, ok := stok["Penggaris"]; ok {
fmt.println("Penggaris =", v)
} else {
fmt.println("kunci Penggaris tidak ada")
}
stok["Penggaris"] = 3
// iterasi map: urutan kunjungan tidak dijamin, sehingga
// contoh hanya menjumlahkan nilai tanpa bergantung urutan
total := 0
for _, jumlah in stok {
total += jumlah
}
fmt.println("total butir:", total)
delete_key(&stok, "Penghapus")
fmt.println("len sesudah delete_key =", len(stok))
}odin run main.odin -filearray asli : [10, 20, 30, 40]
array salinan: [99, 20, 30, 40]
ukuran [4]int = 32 byte
slice s = a[1:3]: [20, 30]
array ikut berubah: [10, 21, 30, 40]
ukuran tipe slice = 16 byte
a[0] = 10
a[1] = 21
a[2] = 30
a[3] = 40
d awal: len = 0 cap = 4
d isi: [1, 4, 9, 16, 25, 36, 49, 64, 81]
d akhir: len = 9 cap = 16
stok Buku: 8
len(stok) = 3
ada kunci Buku? true
kunci Penggaris tidak ada
total butir: 28
len sesudah delete_key = 3Iterasi array menggunakan bentuk for v, i in a yang memberikan nilai terlebih dahulu, lalu indeksnya. Urutan variabel ini berbeda dari map pada bagian akhir, dan keluaran di atas menunjukkan keduanya secara nyata. Semantik nilai array juga berlaku pada parameter proc, sehingga mengirim [4]int ke sebuah proc menyalin seluruh 32 byte ke dalam proc itu. Ukuran array terkunci pada waktu kompilasi dan menjadi bagian dari tipenya. Array [4]int dan [5]int adalah dua tipe yang berbeda, sehingga keduanya tidak dapat ditukar tanpa pernyataan yang eksplisit.
Slice: penunjuk dan panjang, bukan salinan#
Slice ditulis []int tanpa angka dan merupakan pandangan atas data yang sudah ada. Ekspresi a[1:3] membuat slice dari elemen indeks 1 sampai sebelum 3, yaitu indeks awal yang dimasukkan dan indeks akhir yang dilewati. Slice tidak menyimpan elemen itu sendiri, melainkan penunjuk ke elemen pertama beserta panjangnya. Total keduanya 16 byte pada target 64-bit, seperti pada keluaran ukuran tipe slice = 16 byte. Karena penunjuk itu merujuk data a, penulisan s[0] = 21 mengubah elemen a pada indeks 1, dan array aslinya ikut berubah. Perilaku ini berbeda dari penyalinan array tetap pada bagian sebelumnya.
Pemotongan yang sama berlaku untuk dynamic array, sehingga slice dari kedua jenis penyimpanan berurutan berperilaku identik, yaitu tetap merujuk data asalnya. Perlu diingat bahwa alokasi ulang pada dynamic array asal dapat memindahkan datanya, sehingga slice lama perlu dibuat ulang setelah data bertumbuh. Mekanisme pertumbuhan tersebut dibahas pada bagian berikutnya.
Dynamic array: panjang yang bertambah#
Dynamic array ditulis [dynamic]int dan menyimpan data yang panjangnya berubah saat program berjalan. Nilai awalnya dibuat dengan make, misalnya make([dynamic]int, 0, 4) untuk mulai dari panjang nol dengan kapasitas empat. Elemen baru ditambahkan dengan append(&d, nilai), dan proc itu dapat menerima beberapa nilai sekaligus. Alokasi memori yang dilakukan make dan append menggunakan alokator dari context bawaan Odin, dan detail pengaturan alokator dibahas pada modul memori dan alokator.
Keluaran program memperlihatkan mekanisme pertumbuhannya. Kapasitas awal empat mencukupi untuk beberapa elemen pertama. Ketika kapasitas terlampaui, append mengalokasikan ruang baru yang lebih besar, sehingga setelah sembilan kali penambahan kapasitas menjadi 16. Alokasi ulang semacam ini berbiaya, sehingga perkiraan kapasitas awal yang masuk akal mengurangi jumlah perpindahan data. Memori dynamic array dibebaskan dengan delete(d) setelah tidak digunakan.
Map: data berkunci#
Map memetakan kunci ke nilai, dengan tipe ditulis map[string]int untuk kunci string dan nilai int. Map kosong dibuat dengan literal kosong map[string]int{}, kemudian entri diisi dan diubah melalui stok["Buku"] = 8. Kehadiran kunci diuji dengan "Buku" in stok, sedangkan bentuk v, ok := stok["Penggaris"] mengembalikan nilai beserta penanda keberadaan kunci sehingga nilai nol tidak disalahartikan sebagai data. Penghapusan entri menggunakan delete_key(&stok, "Penghapus") yang memerlukan alamat map.
Iterasi map berbentuk for kunci, jumlah in stok, yaitu kunci lebih dulu dan nilai kemudian, kebalikan dari urutan pada array. Urutan kunjungan map tidak dijamin, baik terhadap urutan penambahan entri maupun dari satu eksekusi program ke eksekusi berikutnya, sehingga program tidak boleh bergantung pada urutan tersebut. Contoh pada program menghindari ketergantungan itu dengan hanya menjumlahkan seluruh nilai menjadi 28. Bila urutan keluaran yang stabil diperlukan, kunci dikumpulkan ke slice lalu diurutkan sebelum digunakan untuk membaca nilai.
Ringkasan#
- Array tetap
[N]Tmenyimpan elemen secara langsung, berukuran tetap, dan disalin penuh saat ditugaskan. - Slice
[]Thanyalah penunjuk beserta panjang, sehingga perubahan lewat slice menyentuh data yang dirujuk. - Dynamic array
[dynamic]Ttumbuh melaluiappenddengan kapasitas yang dialokasi ulang, dan dibebaskan dengandelete. - Map
map[K]Vmemetakan kunci ke nilai, diuji denganinatau idiom comma-ok, dan dihapus entrinya dengandelete_key. - Urutan iterasi array memberi nilai lalu indeks, sedangkan map memberi kunci lalu nilai.
Referensi#
- Odin Language Overview — odin-lang.org — Dokumentasi resmi yang mencakup array, slice, dynamic array, dan map beserta idiom comma-ok.
- Package core:fmt — pkg.odin-lang.org — Dokumentasi paket pencetakan yang menampilkan keempat tipe penyimpanan pada contoh.