Struktur data adalah fondasi dalam pemrograman, dan Go menyediakan tiga struktur data collection yang powerful: Arrays, Slices, dan Maps. Ketiga struktur data ini memiliki karakteristik dan kegunaan yang berbeda, namun saling melengkapi dalam membangun aplikasi Go yang efisien. Artikel ini akan membahas secara mendalam tentang cara kerja, perbedaan, dan best practices dalam menggunakan Arrays, Slices, dan Maps di Go. Saya harapkan sudah mempelajari artikel sebelumnya tentang Functions di Go: Parameter, Return Values, dan Named Returns supaya lebih mudah memahami materi ini. Ini merupakan materi terakhir untuk bagian pemula.
Mengapa Struktur Data Collection Penting di Go?
Go dirancang untuk performa dan kesederhanaan. Struktur data collection di Go mencerminkan filosofi ini dengan memberikan:
- Performance yang optimal – Memory layout yang efisien
- Type safety – Compile-time checking untuk mencegah error
- Simplicity – API yang mudah dipahami dan digunakan
- Memory efficiency – Manajemen memori yang baik
Mari kita eksplorasi masing-masing struktur data ini secara detail.
Arrays di Go: Fondasi Structure Data
Apa itu Array di Go?
Array di Go adalah struktur data dengan ukuran tetap yang menyimpan elemen-elemen dengan tipe data yang sama. Berbeda dengan bahasa lain, ukuran array di Go adalah bagian dari tipenya.
Deklarasi dan Inisialisasi Array
gopackage main
import "fmt"
func main() {
// Deklarasi array dengan ukuran 5
var numbers [5]int
fmt.Println("Array kosong:", numbers) // Output: [0 0 0 0 0]
// Inisialisasi dengan nilai
var fruits [3]string = [3]string{"apple", "banana", "orange"}
fmt.Println("Array fruits:", fruits)
// Inisialisasi singkat
colors := [4]string{"red", "green", "blue", "yellow"}
fmt.Println("Array colors:", colors)
// Auto-sizing dengan ...
auto := [...]int{1, 2, 3, 4, 5}
fmt.Println("Auto-sized array:", auto)
fmt.Println("Length:", len(auto)) // Output: 5
}
Operasi Dasar Array
gofunc arrayOperations() {
numbers := [5]int{10, 20, 30, 40, 50}
// Mengakses elemen
fmt.Println("First element:", numbers[0]) // Output: 10
fmt.Println("Last element:", numbers[4]) // Output: 50
// Mengubah nilai elemen
numbers[2] = 100
fmt.Println("Modified array:", numbers) // Output: [10 20 100 40 50]
// Iterasi dengan for loop
fmt.Println("Iterasi dengan index:")
for i := 0; i < len(numbers); i++ {
fmt.Printf("Index %d: %d\n", i, numbers[i])
}
// Iterasi dengan range
fmt.Println("Iterasi dengan range:")
for index, value := range numbers {
fmt.Printf("Index %d: %d\n", index, value)
}
}
Multidimensional Arrays
gofunc multidimensionalArrays() {
// Array 2D - Matrix 3x3
var matrix [3][3]int
// Inisialisasi matrix
matrix = [3][3]int{
{1, 2, 3},
{4, 5, 6},
{7, 8, 9},
}
fmt.Println("Matrix:")
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
fmt.Printf("%d ", matrix[i][j])
}
fmt.Println()
}
// Array 3D
var cube [2][2][2]int = [2][2][2]int{
{
{1, 2},
{3, 4},
},
{
{5, 6},
{7, 8},
},
}
fmt.Println("Cube[0][1][1]:", cube[0][1][1]) // Output: 4
}
Slices di Go: Dynamic Arrays yang Powerful
Apa itu Slice?
Slice adalah abstraksi di atas array yang memberikan fleksibilitas ukuran dinamis. Slice tidak menyimpan data secara langsung, melainkan referensi ke underlying array.
Anatomy Slice
Slice terdiri dari tiga komponen:
- Pointer: Menunjuk ke elemen pertama array yang dapat diakses
- Length: Jumlah elemen dalam slice
- Capacity: Jumlah elemen dalam underlying array
gofunc sliceAnatomy() {
// Membuat slice dari array
arr := [6]int{1, 2, 3, 4, 5, 6}
slice := arr[1:4] // Slice dari index 1 sampai 3
fmt.Printf("Array: %v\n", arr)
fmt.Printf("Slice: %v\n", slice)
fmt.Printf("Length: %d, Capacity: %d\n", len(slice), cap(slice))
// Mengubah slice mempengaruhi array asli
slice[0] = 100
fmt.Printf("Array setelah modifikasi slice: %v\n", arr)
}
Cara Membuat Slice
gofunc createSlices() {
// 1. Slice literal
numbers := []int{1, 2, 3, 4, 5}
fmt.Println("Slice literal:", numbers)
// 2. Menggunakan make()
// make([]type, length, capacity)
made := make([]int, 5, 10)
fmt.Printf("Made slice: %v, len: %d, cap: %d\n", made, len(made), cap(made))
// 3. Dari array
arr := [5]string{"a", "b", "c", "d", "e"}
slice1 := arr[:] // Semua elemen
slice2 := arr[1:4] // Index 1-3
slice3 := arr[:3] // Dari awal sampai index 2
slice4 := arr[2:] // Dari index 2 sampai akhir
fmt.Println("slice1:", slice1)
fmt.Println("slice2:", slice2)
fmt.Println("slice3:", slice3)
fmt.Println("slice4:", slice4)
// 4. Nil slice
var nilSlice []int
fmt.Println("Nil slice:", nilSlice == nil) // Output: true
}
Operasi Slice yang Powerful
gofunc sliceOperations() {
// Append - Menambah elemen
slice := []int{1, 2, 3}
fmt.Println("Original:", slice)
slice = append(slice, 4, 5, 6)
fmt.Println("After append:", slice)
// Append slice ke slice
other := []int{7, 8, 9}
slice = append(slice, other...)
fmt.Println("After append slice:", slice)
// Copy slice
source := []int{1, 2, 3, 4, 5}
dest := make([]int, len(source))
copied := copy(dest, source)
fmt.Printf("Copied %d elements: %v\n", copied, dest)
// Delete element (no built-in delete)
// Delete index 2
slice = []int{1, 2, 3, 4, 5}
index := 2
slice = append(slice[:index], slice[index+1:]...)
fmt.Println("After delete index 2:", slice)
}
Slice Tricks dan Best Practices
gofunc sliceTricks() {
// 1. Reverse slice
slice := []int{1, 2, 3, 4, 5}
for i, j := 0, len(slice)-1; i < j; i, j = i+1, j-1 {
slice[i], slice[j] = slice[j], slice[i]
}
fmt.Println("Reversed:", slice)
// 2. Remove duplicates
numbers := []int{1, 2, 2, 3, 3, 3, 4, 5, 5}
unique := removeDuplicates(numbers)
fmt.Println("Unique:", unique)
// 3. Filter slice
all := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
even := filter(all, func(n int) bool { return n%2 == 0 })
fmt.Println("Even numbers:", even)
// 4. Map transformation
squared := mapTransform([]int{1, 2, 3, 4, 5}, func(n int) int { return n * n })
fmt.Println("Squared:", squared)
}
func removeDuplicates(slice []int) []int {
keys := make(map[int]bool)
var result []int
for _, item := range slice {
if !keys[item] {
keys[item] = true
result = append(result, item)
}
}
return result
}
func filter(slice []int, predicate func(int) bool) []int {
var result []int
for _, item := range slice {
if predicate(item) {
result = append(result, item)
}
}
return result
}
func mapTransform(slice []int, transform func(int) int) []int {
result := make([]int, len(slice))
for i, item := range slice {
result[i] = transform(item)
}
return result
}
Maps di Go: Key-Value Storage yang Efisien
Apa itu Map?
Map adalah struktur data yang menyimpan pasangan key-value. Map di Go mirip dengan hash table atau dictionary di bahasa lain, namun dengan type safety yang ketat.
Deklarasi dan Inisialisasi Map
gofunc mapBasics() {
// 1. Deklarasi map kosong
var ages map[string]int
fmt.Println("Nil map:", ages == nil) // Output: true
// 2. Inisialisasi dengan make
ages = make(map[string]int)
ages["Alice"] = 25
ages["Bob"] = 30
fmt.Println("Ages map:", ages)
// 3. Map literal
scores := map[string]int{
"Math": 95,
"Science": 87,
"English": 92,
}
fmt.Println("Scores:", scores)
// 4. Map dengan berbagai tipe key
complexMap := map[int]string{
1: "One",
2: "Two",
3: "Three",
}
fmt.Println("Complex map:", complexMap)
}
Operasi Dasar Map
gofunc mapOperations() {
students := make(map[string]int)
// Menambah elemen
students["Alice"] = 85
students["Bob"] = 92
students["Charlie"] = 78
fmt.Println("Students:", students)
// Mengakses elemen
aliceScore := students["Alice"]
fmt.Println("Alice's score:", aliceScore)
// Check if key exists
score, exists := students["David"]
if exists {
fmt.Println("David's score:", score)
} else {
fmt.Println("David not found")
}
// Mengubah nilai
students["Alice"] = 90
fmt.Println("Updated Alice's score:", students["Alice"])
// Menghapus elemen
delete(students, "Charlie")
fmt.Println("After deletion:", students)
// Iterasi map
fmt.Println("All students:")
for name, score := range students {
fmt.Printf("%s: %d\n", name, score)
}
}
Map dengan Struct Values
gotype Person struct {
Name string
Age int
Email string
}
func mapWithStructs() {
// Map dengan struct sebagai value
employees := map[int]Person{
1001: {Name: "Alice", Age: 30, Email: "alice@company.com"},
1002: {Name: "Bob", Age: 25, Email: "bob@company.com"},
1003: {Name: "Charlie", Age: 35, Email: "charlie@company.com"},
}
fmt.Println("Employees:")
for id, person := range employees {
fmt.Printf("ID: %d, Name: %s, Age: %d\n", id, person.Name, person.Age)
}
// Update struct field
emp := employees[1001]
emp.Age = 31
employees[1001] = emp // Harus assign kembali karena struct is value type
fmt.Println("Updated Alice's age:", employees[1001].Age)
}
Map dengan Pointer Values
gofunc mapWithPointers() {
// Map dengan pointer ke struct
employees := make(map[int]*Person)
employees[1001] = &Person{Name: "Alice", Age: 30, Email: "alice@company.com"}
employees[1002] = &Person{Name: "Bob", Age: 25, Email: "bob@company.com"}
// Update melalui pointer - lebih efisien
employees[1001].Age = 31
fmt.Println("Alice's updated age:", employees[1001].Age)
// Check nil pointer
if emp := employees[1003]; emp != nil {
fmt.Println("Employee 1003:", emp.Name)
} else {
fmt.Println("Employee 1003 not found")
}
}
Perbandingan: Arrays vs Slices vs Maps
Kapan Menggunakan Array?
gofunc whenToUseArrays() {
// 1. Ukuran data tetap dan diketahui
daysInWeek := [7]string{"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"}
// 2. Performance critical code
var buffer [1024]byte // Fixed-size buffer
// 3. Matrix operations
var matrix [3][3]float64
fmt.Println("Days:", daysInWeek)
fmt.Println("Buffer size:", len(buffer))
fmt.Println("Matrix dimensions:", len(matrix), "x", len(matrix[0]))
}
Kapan Menggunakan Slice?
gofunc whenToUseSlices() {
// 1. Dynamic data - ukuran berubah-ubah
var dynamicList []string
dynamicList = append(dynamicList, "item1", "item2")
// 2. Function parameters - lebih fleksibel
processItems([]int{1, 2, 3, 4, 5})
// 3. Sebagai result dari function
result := generateNumbers(10)
fmt.Println("Generated:", result)
// 4. Working dengan unknown size
lines := readLinesFromFile("example.txt") // Returns []string
fmt.Println("Lines count:", len(lines))
}
func processItems(items []int) {
for _, item := range items {
fmt.Printf("%d ", item)
}
fmt.Println()
}
func generateNumbers(n int) []int {
result := make([]int, n)
for i := 0; i < n; i++ {
result[i] = i + 1
}
return result
}
func readLinesFromFile(filename string) []string {
// Simulasi reading file
return []string{"line1", "line2", "line3"}
}
Kapan Menggunakan Map?
gofunc whenToUseMaps() {
// 1. Key-value relationships
userRoles := map[string]string{
"alice": "admin",
"bob": "user",
"charlie": "moderator",
}
// 2. Caching/Memoization
cache := make(map[string]interface{})
cache["user:123"] = Person{Name: "Alice", Age: 30}
// 3. Counting/Frequency
wordCount := countWords("hello world hello go world")
fmt.Println("Word count:", wordCount)
// 4. Fast lookups
validIDs := map[int]bool{
1001: true,
1002: true,
1003: true,
}
if validIDs[1001] {
fmt.Println("ID 1001 is valid")
}
}
func countWords(text string) map[string]int {
words := []string{"hello", "world", "hello", "go", "world"} // Simplified
count := make(map[string]int)
for _, word := range words {
count[word]++
}
return count
}
Kesimpulan
Struktur data collection di Go – Arrays, Slices, dan Maps – masing-masing memiliki keunggulan dan use case yang spesifik:
Arrays
- Keunggulan: Performance optimal, memory layout predictable, type safe
- Kapan digunakan: Data dengan ukuran tetap, buffer, matrix operations
- Perhatian: Ukuran adalah bagian dari tipe, tidak fleksibel
Slices
- Keunggulan: Fleksibel, dynamic sizing, powerful built-in functions
- Kapan digunakan: Data collection yang berubah-ubah, function parameters, API responses
- Perhatian: Growth strategy, memory leaks pada large slices
Maps
- Keunggulan: Fast lookup O(1), key-value relationship, flexible key types
- Kapan digunakan: Caching, counting, fast lookups, configuration
- Perhatian: Iteration order tidak guaranteed, nil map panic, memory overhead
Selanjutnya kita akan masuk ke Intermediate Topics.
Seri Belajar Golang – Pemula
- Pengenalan Golang: Mengapa Google Menciptakan Bahasa Ini?
- Setup Environment dan Hello World di Go
- Sintaks Dasar Go: Variables, Constants, dan Data Types
- Control Flow di Go: If, Switch, dan Loops
- Functions di Go: Parameter, Return Values, dan Named Returns
- Arrays, Slices, dan Maps: Struktur Data Penting di Go
Pingback: Control Flow di Go: If, Switch, dan Loops - Panduan Lengkap Struktur Kontrol
Pingback: Sintaks Dasar Go: Variables, Constants, dan Data Types - Panduan Lengkap
Pingback: Cara Install Golang dan Membuat Program Hello World Pertama