Banyak data nyata berbentuk grid: papan permainan, tabel penjualan, piksel citra, atau matriks pada perhitungan numerik. Satu dimensi tidak cukup untuk merepresentasikan struktur tersebut, sehingga Go memungkinkan kita menyusun slice di dalam slice. Pada chapter ini kita membangun matriks 3×3 dengan make, menulis data tabel dengan panjang baris yang berbeda-beda, lalu menghindari satu jebakan klasik yang membuat seluruh baris berubah serentak padahal kita hanya ingin mengubah satu sel.

Memahami Slice Bersarang #

Tipe [][]int dibaca dari luar ke dalam: slice yang setiap elemennya adalah slice []int. Elemen tabel[0] adalah baris pertama bertipe []int, dan sel tunggal diakses dengan dua indeks berurutan seperti tabel[1][2]. Berbeda dengan array multidimensi [3][3]int yang seluruh ukurannya terkunci, [][]int membebaskan setiap baris memiliki panjang sendiri — sifat yang disebut jagged rows dan cocok untuk data yang tidak berbentuk persegi.

Membangun Matriks dengan make #

make([][]int, 3) hanya menyiapkan tiga slot untuk baris, dan setiap slot berisi slice nil. Setiap baris kemudian harus dialokasikan sendiri dengan make([]int, 3) sebelum bisa diisi — penulisan ke matriks[0][0] sebelum baris pertama dibuat akan memicu panic karena mencoba mengindeks slice nil. Pola lengkapnya adalah loop yang mengalokasikan baris lalu mengisi sel:

go
matriks := make([][]int, 3)
for i := range matriks {
	matriks[i] = make([]int, 3)
	for j := range matriks[i] {
		matriks[i][j] = i*3 + j + 1
	}
}

Rumus i*3 + j + 1 mengisi angka 1 sampai 9 secara berurutan sehingga matriks terisi deterministik: baris pertama [1 2 3], baris kedua [4 5 6], baris ketiga [7 8 9].

Baris dengan Panjang Berbeda #

Literal bersarang memungkinkan setiap baris ditulis langsung dengan panjang yang bebas. Contoh berikut membentuk segitiga angka dengan baris berpanjang satu, dua, dan tiga:

go
segitiga := [][]int{
	{1},
	{1, 2},
	{1, 2, 3},
}

Slice segitiga tetap satu tipe [][]int meski panjang barisnya tidak seragam, dan len(segitiga[i]) melaporkan panjang setiap baris. Bentuk ini sering dipakai untuk data riil seperti daftar pesanan per pelanggan yang jumlahnya berbeda.

Jebakan: Satu Slice Dipakai Semua Baris #

Karena elemen baris adalah slice biasa, jebakan aliasing dari chapter Backing Array berlaku juga di sini. Kalau kita mengalokasikan satu slice lalu menugaskannya ke setiap baris, semua baris menunjuk penyimpanan yang sama; penulisan satu sel akan mengubah sel yang sama di seluruh baris.

Program Utuh #

Berkas matriks-demo.go memuat ketiga skenario sekaligus, termasuk bukti nyata jebakan baris bersama. Jalankan dengan go run matriks-demo.go dari direktori chapter:

go
package main

import "fmt"

func main() {
	// Slice bersarang: alokasikan setiap baris dengan make.
	matriks := make([][]int, 3)
	for i := range matriks {
		matriks[i] = make([]int, 3)
		for j := range matriks[i] {
			matriks[i][j] = i*3 + j + 1
		}
	}
	fmt.Println("matriks      :", matriks)
	fmt.Println("matriks[1][2]:", matriks[1][2])
	fmt.Println("matriks[2]   :", matriks[2])

	// Literal slice bersarang: panjang tiap baris boleh berbeda.
	segitiga := [][]int{
		{1},
		{1, 2},
		{1, 2, 3},
	}
	fmt.Println("segitiga     :", segitiga)
	for i, baris := range segitiga {
		fmt.Printf("len baris %d  = %d\n", i, len(baris))
	}

	// Jebakan: satu slice dipakai ulang untuk semua baris.
	satuBaris := make([]int, 3)
	jebakan := make([][]int, 3)
	for i := range jebakan {
		jebakan[i] = satuBaris
	}
	jebakan[0][0] = 42
	fmt.Println("jebakan      :", jebakan)
}

Output yang Dihasilkan:

text
matriks      : [[1 2 3] [4 5 6] [7 8 9]]
matriks[1][2]: 6
matriks[2]   : [7 8 9]
segitiga     : [[1] [1 2] [1 2 3]]
len baris 0  = 1
len baris 1  = 2
len baris 2  = 3
jebakan      : [[42 0 0] [42 0 0] [42 0 0]]

Mari kita telaah baris demi baris program di atas:

  1. Baris 7 (matriks := make([][]int, 3)): menyiapkan tiga slot baris; setiap slot masih berisi slice nil sampai diisi loop.
  2. Baris 9 (matriks[i] = make([]int, 3)): mengalokasikan penyimpanan terpisah untuk setiap baris — kunci menghindari jebakan aliasing.
  3. Baris 11 (matriks[i][j] = i*3 + j + 1): mengisi sel dengan rumus baris-kolom sehingga matriks berisi angka 1 sampai 9 yang berurutan.
  4. Baris 15–17: matriks[1][2] membaca sel baris kedua kolom ketiga bernilai 6, dan matriks[2] mengambil baris ketiga utuh [7 8 9].
  5. Baris 20–25 (literal segitiga): tiga baris berbeda panjang dalam satu tipe [][]int; loop berikut mencetak panjang 1, 2, dan 3.
  6. Baris 32–40 (satuBaris): ketiga baris jebakan menunjuk slice yang sama, terbukti dari output [[42 0 0] [42 0 0] [42 0 0]] setelah hanya satu penulisan jebakan[0][0] = 42.

Kita kini mampu menyusun data dua dimensi dengan aman. Seluruh struktur sejauh ini — array maupun slice — tetap mengakses data lewat posisi (indeks). Bab berikutnya memperkenalkan struktur dengan cara akses yang sama sekali berbeda: map dengan key-value.

Referensi #

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

matriks-demo.go

Unduh berkas
go
package main

import "fmt"

func main() {
	// Slice bersarang: alokasikan setiap baris dengan make.
	matriks := make([][]int, 3)
	for i := range matriks {
		matriks[i] = make([]int, 3)
		for j := range matriks[i] {
			matriks[i][j] = i*3 + j + 1
		}
	}
	fmt.Println("matriks      :", matriks)
	fmt.Println("matriks[1][2]:", matriks[1][2])
	fmt.Println("matriks[2]   :", matriks[2])

	// Literal slice bersarang: panjang tiap baris boleh berbeda.
	segitiga := [][]int{
		{1},
		{1, 2},
		{1, 2, 3},
	}
	fmt.Println("segitiga     :", segitiga)
	for i, baris := range segitiga {
		fmt.Printf("len baris %d  = %d\n", i, len(baris))
	}

	// Jebakan: satu slice dipakai ulang untuk semua baris.
	satuBaris := make([]int, 3)
	jebakan := make([][]int, 3)
	for i := range jebakan {
		jebakan[i] = satuBaris
	}
	jebakan[0][0] = 42
	fmt.Println("jebakan      :", jebakan)
}