Array

Array berukuran tetap dan alasan jarang dipakai secara langsung.

Daftar isi

Array adalah kumpulan elemen berukuran tetap yang seluruhnya bertipe sama, dan panjangnya menyatu dengan tipe array itu sendiri. Topik ini membahas cara mendeklarasikan dan mengisi array, aturan literal array, penyalinan seluruh isinya saat ditugaskan atau dikirim ke fungsi, serta alasan array jarang digunakan secara langsung pada kode Go sehari-hari. Pemahaman ini menjadi fondasi bagi pembahasan slice, yaitu kumpulan data berukuran fleksibel yang dibangun di atas array.

Deklarasi, pengisian, dan literal array#

Tipe array ditulis [N]T, yaitu panjang di dalam kurung siku diikuti tipe elemennya. Deklarasi var nilai [4]int membuat empat elemen int yang semuanya berisi zero value, yaitu nilai nol yang ditetapkan untuk tipe masing-masing elemen. Elemen diakses lewat indeks yang dimulai dari 0 hingga panjang dikurangi satu, sedangkan fungsi len menghitung banyak elemennya. Program berikut mengisi empat nilai ujian satu per satu, lalu membacanya kembali:

go dasar.go
package main

import "fmt"

func main() {
	var nilai [4]int
	nilai[0] = 80
	nilai[1] = 75
	nilai[2] = 90
	nilai[3] = 65

	fmt.Println("Isi array :", nilai)
	fmt.Println("Banyaknya :", len(nilai))
	fmt.Printf("Tipe      : %T\n", nilai)

	total := 0
	for _, v := range nilai {
		total += v
	}
	fmt.Println("Jumlah    :", total)
}
text Keluaran
Isi array : [80 75 90 65]
Banyaknya : 4
Tipe      : [4]int
Jumlah    : 310

Baris fmt.Printf dengan verb %T menampilkan tipe lengkap variabelnya, yaitu [4]int. Angka empat ikut tercetak karena panjang array memang bagian dari tipe, bukan properti yang disimpan terpisah. Perulangan range menjelajah elemen tanpa perlu menulis batas indeks secara manual, dan tanda _ membuang indeks yang tidak dipakai.

Program dijalankan dari folder kerja tetap di luar pohon konten:

bash
# siapkan folder kerja untuk topik ini
mkdir -p /tmp/gotipe/array
cp dasar.go literal.go salin.go /tmp/gotipe/array/
cd /tmp/gotipe/array

# periksa format sebelum menjalankan programnya
gofmt -l .

# jalankan programnya
go run dasar.go
text Keluaran
Isi array : [80 75 90 65]
Banyaknya : 4
Tipe      : [4]int
Jumlah    : 310

Array juga dapat dibuat sekaligus dengan composite literal, yaitu notasi kurung kurawal yang memuat nilai awal elemen. Bentuk [3]float64{25.5, 27.0, 24.5} menyebut tipe dan panjangnya secara eksplisit, sedangkan tanda ... pada [...]string{...} menyuruh kompiler menghitung panjang dari banyak nilai yang ditulis. Hasil keduanya tetap bertipe array, bukan slice, sehingga angka panjangnya tetap menyatu dengan tipenya:

go literal.go
package main

import "fmt"

func main() {
	suhu := [3]float64{25.5, 27.0, 24.5}
	hari := [...]string{"Senin", "Selasa", "Rabu", "Kamis", "Jumat"}

	fmt.Println(suhu)
	fmt.Printf("tipe hari: %T, banyaknya: %d\n", hari, len(hari))
}
text Keluaran
[25.5 27 24.5]
tipe hari: [5]string, banyaknya: 5

Panjang array menjadi bagian dari tipe#

Panjang array ditetapkan pada waktu kompilasi dan tidak dapat berubah selama program berjalan. Konsekuensinya, [3]int dan [4]int adalah dua tipe yang berbeda sama sekali meskipun tipe elemennya sama. Penugasan antar keduanya ditolak kompiler, demikian pula pemakaian [3]int sebagai parameter bagi nilai [4]int. Potongan berikut gagal dikompilasi karena perbedaan panjang tersebut:

go Panjang berbeda berarti tipe berbeda
// tidak-dijalankan
var pendek [3]int
var panjang [4]int = pendek // error kompilasi: [3]int tidak dapat ditugaskan ke [4]int

Sifat tetap ini juga berarti array tidak memiliki operasi penambahan elemen. Fungsi append hanya berlaku untuk slice, sehingga kebutuhan kumpulan data yang bertambah seiring berjalannya program harus dijawab dengan slice. Di sinilah letak alasan utama array jarang digunakan secara langsung, yaitu ukurannya yang beku sejak awal.

Kesetaraan tipe itu juga membawa satu keuntungan, yaitu perbandingan yang langsung. Dua array yang tipe dan elemennya sama dapat dibandingkan langsung dengan ==, yang memeriksa elemen demi elemen tanpa perulangan manual. Syaratnya, tipe elemennya juga harus dapat dibandingkan, sehingga array int sah, sedangkan array yang memuat slice tidak. Sifat inilah yang kelak membuat array dipakai sebagai kunci peta, karena kunci peta menuntut tipe yang dapat dibandingkan.

Array disalin utuh sebagai nilai#

Array di Go berperilaku sebagai satu nilai utuh. Penugasan array ke variabel lain maupun pengirimannya sebagai parameter fungsi menyalin seluruh elemennya, bukan sekadar menunjuk data aslinya. Perilaku ini berbeda dengan kebiasaan banyak bahasa lain yang mengirim array melalui referensi. Program berikut membuktikannya dengan fungsi yang mengosongkan isinya:

go salin.go
package main

import "fmt"

// reset menerima salinan array, sehingga perubahan di dalam fungsi
// tidak terlihat oleh pemanggil.
func reset(data [3]int) {
	for i := range data {
		data[i] = 0
	}
}

func main() {
	stok := [3]int{12, 7, 30}
	reset(stok)
	fmt.Println("Setelah reset:", stok)

	hari := [...]string{"Senin", "Selasa", "Rabu", "Kamis"}
	fmt.Printf("Tipe hari: %T\n", hari)
}
text Keluaran
Setelah reset: [12 7 30]
Tipe hari: [4]string

Fungsi reset menerima salinan [3]int, mengosongkan salinan itu, dan selesai tanpa hasil apa pun. Nilai stok milik pemanggil tetap utuh karena yang berubah hanyalah salinannya. Baris terakhir pada main sekaligus memperlihatkan literal [...]string yang panjangnya disimpulkan menjadi empat oleh kompiler. Hasil penyimpulan itu terbaca pada tipe [4]string yang dicetak.

Selain bekerja sebagai dasar dari slice, array tetap dipakai pada peran yang cocok dengan ukuran tetapnya, misalnya penampung dua belas bulan atau empat arah mata angin. Pustaka standar juga memakai array ketika ukurannya memang ditetapkan, misalnya hasil sha256.Sum256 yang bertipe [32]byte. Untuk kebutuhan kumpulan data pada umumnya, slice menjadi pilihan bawaan, dan array bekerja di belakangnya sebagai tempat data benar-benar disimpan.

Kesalahan umum#

  1. Mengindeks melewati batas. Ekspresi nilai[4] pada array [4]int memicu panic index out of range saat program berjalan. Perbaikannya, pastikan indeks selalu kurang dari len(nilai), atau jelajah elemen dengan range yang batasnya diurus otomatis.
  2. Mencampur array dengan panjang berbeda. Tipe [3]int dan [4]int tidak dapat ditugaskan satu sama lain dan ditolak kompiler. Perbaikannya, samakan panjang keduanya, atau gunakan slice apabila panjang memang harus fleksibel.
  3. Mengira array dikirim dengan referensi. Parameter array menerima salinan utuh, sehingga perubahan di dalam fungsi tidak terlihat oleh pemanggil. Perbaikannya, kirim pointer *[N]T apabila perubahan memang diinginkan, atau gunakan slice.

Ringkasan#

  • Array berukuran tetap dan panjangnya menyatu dengan tipenya, sehingga [3]int dan [4]int adalah tipe yang berbeda.
  • Penugasan dan pengiriman array menyalin seluruh elemennya sebagai satu nilai utuh.
  • Literal [...]T menyuruh kompiler menghitung panjang dari banyak nilai yang ditulis.
  • Array jarang digunakan secara langsung karena ukurannya beku dan salinannya mahal, dan perannya diambil alih slice.

Referensi#