
Go From Zero to Hero #16: Slice di Go — Cara Mengatasi Ukuran Statis Array dengan Struktur Data Dinamis
- Daffa Fadhil
- Golang, Code
- 8 Juli 2026
Table of Contents
Halo Go Developers! Selamat datang kembali di seri Go From Zero to Hero.
Di artikel sebelumnya, kita sudah berkenalan dengan Array dan tahu cara menyimpan sekumpulan data dalam satu variabel. Tapi, jujur saja, kamu pasti merasa ada yang mengganjal, kan? Sifat Array di Go itu sangat kaku karena ukurannya statis alias mutlak. Sekali kamu bikin Array isi 5, ukurannya tidak akan bisa ditambah menjadi 6 atau dikurangi menjadi 3 di tengah jalan.
Di dunia nyata, data kita itu dinamis. Jumlah user yang login bisa bertambah, isi keranjang belanjaan bisa dikurangi, dan kita butuh sesuatu yang lebih fleksibel.
Nah, di sinilah Slice datang sebagai solusi! Sederhananya, Slice adalah versi “karet” alias dinamis dari Array. Ukurannya bisa melar dan menyusut secara otomatis sesuai kebutuhan kita. Hebatnya lagi, di balik layar, Slice sebenarnya bekerja dengan cara “meminjam” atau mereferensikan data dari Array penampung.
Yuk, kita bedah bagaimana cara kerja, sintaksis, dan keajaiban trik manipulasi data menggunakan Slice di Go!
Mengapa Array Saja Tidak Cukup?
Sebelum masuk ke sintaksis Slice, mari kita lihat alasan mengapa Array di Go sering membuat developer gemas: ukurannya bersifat final dan tidak bisa diganggu gugat.
package main
import "fmt"
func main() {
// Membuat array dengan kapasitas pas 3 elemen
menuCafe := [3]string{"Kopi", "Teh", "Susu"}
// Kamu TIDAK BISA menambah menu keempat seperti ini:
// menuCafe[3] = "Boba" // Error: index out of bounds
}
Jika kamu memaksa memasukkan data melebihi ukurannya, program kamu akan protes dan memicu error saat di-kompilasi. Di sinilah Slice masuk untuk memotong semua keterbatasan itu.
Membedah Anatomi Slice
Seperti yang sudah disinggung di awal, Slice tidak menyimpan data secara mandiri. Di balik layar, Slice sebenarnya adalah sebuah “jendela” (view) yang menunjuk ke sebuah Array tersembunyi yang disebut Underlying Array.
Setiap kali kamu membuat Slice, Go akan membungkusnya dengan struktur data kecil yang berisi 3 komponen penting:
Pointer: Alamat memori yang menunjuk ke elemen pertama tempat Slice dimulai pada Underlying Array.
Length (Panjang): Jumlah elemen yang saat ini aktif di dalam Slice. Kamu bisa mengeceknya dengan fungsi bawaan len().
Capacity (Kapasitas): Jumlah maksimal elemen yang bisa ditampung oleh Slice sebelum dia terpaksa “minta tempat baru” (memperbesar Underlying Array). Kamu bisa mengeceknya dengan fungsi bawaan cap().
3 Cara Populer Membuat Slice
Di dunia nyata, ada tiga cara utama yang paling sering digunakan para backend engineer untuk membuat Slice di Go:
1. Slice Literal (Cara Langsung)
Cara ini sekilas mirip sekali dengan membuat Array. Bedanya, kamu tidak perlu menuliskan angka ukuran di dalam kurung siku []. Biarkan kurung sikunya kosong melompong.
// Ini ARRAY (ada ukurannya)
angkaArray := [3]int{10, 20, 30}
// Ini SLICE (kosong ukurannya)
angkaSlice := []int{10, 20, 30}
2. Menggunakan Fungsi make()
Jika kamu sudah bisa memperkirakan berapa banyak data yang akan masuk demi menghemat alokasi memori RAM, gunakan fungsi bawaan make().
// Format: make([]tipeData, length, capacity)
keranjang := make([]string, 3, 5)
Artinya: Kamu membuat Slice dengan isi awal 3 elemen kosong (["", “”, “”]), tetapi memori di balik layar sudah memesan tempat aman hingga 5 elemen.
3. Memotong Array (Slicing)
Kamu juga bisa membuat Slice dengan cara menyayat atau mengambil sebagian data dari Array yang sudah ada menggunakan rumus array[low:high].
package main
import "fmt"
func main() {
bulan := [5]string{"Jan", "Feb", "Mar", "Apr", "Mei"}
// Mengambil data dari indeks ke-1 sampai SEBELUM indeks ke-4
kuartalSatu := bulan[1:4]
fmt.Println(kuartalSatu) // Output: [Feb Mar Apr]
}
Slicing: Memotong Slice dari Array (array[low:high])
Untuk melakukan potongan (slicing), Go menggunakan sintaksis yang sangat simpel namun punya satu aturan unik yang sering kali membuat para pemula terkecoh: array[low:high].
Aturan main wajibnya adalah: Elemen pada indeks low akan dibawa (inklusif), tetapi elemen pada indeks high akan ditinggalkan (eksklusif). Dengan kata lain, data yang berhasil diambil adalah data dari indeks low sampai dengan high - 1.
Yuk, kita lihat contoh kodenya agar langsung terbayang di kepala:
package main
import "fmt"
func main() {
// Kita punya Array berisi 6 elemen
hari := [6]string{"Senin", "Selasa", "Rabu", "Kamis", "Jumat", "Sabtu"}
// Kita potong dari indeks ke-1 sampai SEBELUM indeks ke-4
hariKerja := hari[1:4]
fmt.Println(hariKerja)
// Output: [Selasa Rabu Kamis]
// Indeks 1 (Selasa), 2 (Rabu), dan 3 (Kamis) ikut terbawa.
// Indeks 4 (Jumat) TIDAK ikut terbawa karena batas high eksklusif!
}
Simpelnya kamu perlu menambahkan satu indeks di bagian belakang dari rentang indeks yang akan kamu ambil. misal, kamu mau mengambil 3 indeks dimulai dari indeks ke 2 maka untuk menampilkan 3 indeks (yaitu 2,3,4) kamu harus menulisnya [2:5] menambahkan 1 indeks di belakang.
⚡ Trik Cepat Slicing (Sintaks Pintas)
Kerennya lagi, Go memberikan fleksibilitas ekstra di mana kamu tidak wajib mengisi kedua angka low dan high tersebut. Kamu bisa mengosongkan salah satu atau bahkan keduanya untuk melakukan trik pintas berikut:
- array[low:] (Tanpa angka belakang) Maksudnya adalah mengambil data mulai dari indeks low sampai ke seluruh elemen paling ujung terakhir tanpa terkecuali.
// Mengambil dari indeks ke-4 sampai habis
weekend := hari[4:]
fmt.Println(weekend) // Output: [Jumat Sabtu]
- array[:high] (Tanpa angka depan) Maksudnya adalah mengambil data secara otomatis mulai dari indeks paling awal (indeks 0) sampai sebelum indeks high.
// Mengambil dari awal sampai sebelum indeks ke-3
awalMinggu := hari[:3]
fmt.Println(awalMinggu) // Output: [Senin Selasa Rabu]
- array[:] (Kosong dua-duanya) Trik ini digunakan jika kamu ingin mengambil seluruh isi array secara utuh dan mengubah format wadahnya menjadi sebuah Slice baru.
// Mengkloning seluruh isi array menjadi slice
semuaHari := hari[:]
fmt.Println(semuaHari) // Output: [Senin Selasa Rabu Kamis Jumat Sabtu]
Menambah Data Secara Dinamis dengan append()
Inilah fitur paling keren milik Slice. Jika keranjang belanjaan atau data user bertambah, kamu tinggal gunakan fungsi append(). Fungsi ini akan menyelipkan data baru di urutan paling belakang secara otomatis.
package main
import "fmt"
func main() {
var kumpulanNama []string // Membuat slice kosong
// Menambah satu data
kumpulanNama = append(kumpulanNama, "Budi")
// Menambah banyak data sekaligus
kumpulanNama = append(kumpulanNama, "Andi", "Siti")
fmt.Println(kumpulanNama) // Output: [Budi Andi Siti]
}
Bagaimana Cara Kerja append() di Balik Layar? Mungkin kamu bertanya-tanya, “Kalau Slice tidak punya tempat penyimpanan sendiri dan numpang di Array, kok ukurannya bisa melar saat di-append?”
Di sinilah letak kejeniusan bahasa Go. Ketika kamu memanggil append(), Go akan mengecek komponen Kapasitas (cap) dari Slice tersebut:
Jika Kapasitas Masih Cukup: Go langsung memasukkan data baru ke Underlying Array-nya.
Jika Kapasitas Sudah Penuh: Go secara otomatis akan membuat Underlying Array baru di dalam memori RAM yang ukurannya 2x lipat lebih besar. Setelah itu, Go menyalin (copy) semua data lama ke Array baru tersebut, lalu menyelipkan data barumu di akhir.
Hebatnya, semua proses alokasi memori yang rumit itu diurus oleh Go secara otomatis tanpa perlu kita coding manual satu baris pun!
Hati-Hati: Efek Berbagi Memori (Shared Underlying Array)
Karena sifat Slice yang hanya bertindak sebagai “jendela” atau referensi ke Array di bawahnya, ada satu jebakan maut yang sering membuat aplikasi bug jika kamu tidak hati-hati saat melakukan slicing.
Jika kamu membuat Slice baru dari hasil memotong Array (atau dari Slice lain), kedua variabel tersebut sebenarnya sedang berbagi alamat memori yang sama! Artinya, jika kamu mengubah isi data di Slice potongan, data di Array aslinya pun akan ikut berubah secara permanen.
Mari kita buktikan lewat kode berikut:
package main
import "fmt"
func main() {
// Array asli
pakaian := [3]string{"Kemeja", "Kaos", "Jaket"}
// Membuat slice dari hasil potong array pakaian
slicePakaian := pakaian[0:2] // Isi: [Kemeja, Kaos]
// Kita ubah elemen pertama di slicePakaian
slicePakaian[0] = "Hodie"
// Mari kita cek efeknya!
fmt.Println("Isi Slice:", slicePakaian) // Output: [Hodie Kaos]
fmt.Println("Isi Array Asli:", pakaian) // Output: [Hodie Kaos Jaket] -> IKUT BERUBAH!
}
Solusi: Gunakan Fungsi copy() Jika Ingin Aman Jika kebutuhan kamu adalah benar-benar ingin menduplikasi atau mengkloning data ke Slice baru tanpa mau mengganggu data lama, jangan gunakan teknik slicing [:]. Gunakan fungsi bawaan Go yang bernama copy().
Fungsi copy() akan menyalin data secara murni ke alamat memori yang benar-benar baru dan terpisah.
package main
import "fmt"
func main() {
asal := []int{1, 2, 3}
// 1. Wajib buat slice tujuan dengan panjang (len) yang sama terlebih dahulu
tujuan := make([]int, len(asal))
// 2. Lakukan copy data (format: copy(tujuan, asal))
copy(tujuan, asal)
// 3. Sekarang jika tujuan diubah, asal akan tetap aman
tujuan[0] = 99
fmt.Println("Slice Asal:", asal) // Output: [1, 2, 3] (Aman!)
fmt.Println("Slice Tujuan:", tujuan) // Output: [99, 2, 3]
}
Tugas atau Tantangan
Setelah materi kurang rasanya kalau kamu tidak mencoba sendiri. untuk mengetes pemahaman kamu coba kerjakan 2 tantangan berikut ini:
🏋️ Tantangan 1: Kasus Keranjang Belanja (Dasar append)
Kamu diminta untuk membuat sebuah sistem kasir kecil untuk toko buku.
Buatlah sebuah Slice kosong bernama keranjang dengan tipe data string.
Tambahkan buku “Laskar Pelangi” ke dalam keranjang.
Tambahkan dua buku sekaligus: “Bumi Manusia” dan “Filosofi Teras” ke dalam keranjang menggunakan fungsi append.
Cetak isi keranjang beserta jumlah barang yang ada di dalamnya menggunakan fungsi len().
✂️ Tantangan 2: Potong Kue (Menguji Rumus Slicing-mu)
Gunakan rumus pintas yang sudah kita bahas tadi (tambah 1 di rentang indeks belakang) untuk menyelesaikan kasus ini.
Diberikan sebuah Array berikut:
kue := [6]string{"Donat", "Brownies", "Cupcake", "Kelepon", "Martabak", "Croissant"}
Tugasmu:
Potong Array tersebut untuk mengambil data “Cupcake” dan “Kelepon” saja. Simpan di variabel kueTradisional.
Potong Array tersebut untuk mengambil data dari awal hingga sebelum “Kelepon”. Simpan di variabel kueModern.
Cetak kedua variabel tersebut ke layar.
Kesimpulan
Sampai di titik ini, kita bisa menyimpulkan bahwa Slice adalah salah satu senjata paling penting di Go-lang yang wajib kamu kuasai luar kepala. Dengan sifatnya yang dinamis, hemat memori (karena hanya mengoper pointer alamat memori tanpa menduplikasi data), dan fleksibel, Slice sukses mengeliminasi sifat kaku dari Array.
Tidak heran jika di dunia industri nyata, 99% backend engineer akan selalu memilih menggunakan Slice daripada Array untuk mengelola kumpulan data pada projek mereka.
Mari kita review singkat perbedaan core antara Array dan Slice:
- Array: Ukurannya statis (fixed), ditulis dengan ukuran
[3]int, dan jika dioper ke fungsi akan menyalin seluruh datanya (boros memori). - Slice: Ukurannya dinamis (bisa melar), ditulis tanpa ukuran
[]int, bekerja sebagai “jendela” dari Underlying Array, dan sangat hemat memori karena menggunakan sistem pointer.
Next Teaser: Menyimpan Data Unik dengan Map!
Kita sudah bisa menyimpan data list dinamis yang berurutan menggunakan Slice. Tapi, bagaimana jika kita ingin menyimpan data dengan format Key-Value seperti JSON? Sebuah struktur data yang mempermudah kita memanggil nilai secara instan berdasarkan nama kuncinya (misal: mencari umur user menggunakan kata kunci "budi").
Di artikel berikutnya, Go From Zero to Hero #10, kita akan membedah struktur data bawaan Go lainnya yang tidak kalah sakti dan sering menjadi duet maut bersama Slice, yaitu Map.
Sampai jumpa di artikel selanjutnya, jangan lupa praktekkan kodenya di local kalian, dan happy coding!