این جزوه به بررسی دادهساختارهای اصلی در Go (آرایهها، اسلایسها، مپها، رشتهها، ساختارها، و رابطها) و مدیریت حافظه (اشارهگرها، garbage collection، و بهینهسازی) میپردازد. هر بخش با توضیحات مفهومی، مثالهای عملی، نکات پیشرفته، و خطاهای رایج همراه است.
Go دارای دادهساختارهای داخلی قدرتمندی است که برای حل مسائل مختلف طراحی شدهاند. در این بخش، هر دادهساختار به طور کامل بررسی میشود.
آرایه یک مجموعه با اندازه ثابت از عناصر با نوع یکسان است. اندازه آرایه در زمان تعریف مشخص میشود و قابل تغییر نیست.
var arr [3]int // آرایهای با 3 عدد صحیح
arr := [3]int{1, 2, 3} // مقداردهی مستقیم
package main
import "fmt"
func main() {
var arr [4]int
arr[0] = 10
arr[1] = 20
fmt.Println(arr) // خروجی: [10 20 0 0]
arr2 := [3]string{"Ali", "Bob", "Cathy"}
for i, v := range arr2 {
fmt.Printf("Index: %d, Value: %s\n", i, v)
}
}
func modifyArray(arr *[3]int) {
arr[0] = 100
}
func main() {
arr := [3]int{1, 2, 3}
modifyArray(&arr)
fmt.Println(arr) // خروجی: [100 2 3]
}
panic: runtime error: index out of range
میشود.اسلایس یک نمای پویا از یک آرایه است که اندازه آن میتواند تغییر کند. اسلایسها پرکاربردترین دادهساختار در Go هستند.
var slice []int // اسلایس خالی
slice := []int{1, 2, 3} // مقداردهی مستقیم
slice := make([]int, 5, 10) // طول 5، ظرفیت 10
اسلایس شامل سه بخش است:
slice := []int{1, 2}
slice = append(slice, 3) // خروجی: [1 2 3]
slice := []int{1, 2, 3, 4}
sub := slice[1:3] // خروجی: [2 3]
src := []int{1, 2, 3}
dst := make([]int, len(src))
copy(dst, src) // کپی کامل
package main
import "fmt"
func main() {
slice := []int{1, 2, 3}
slice = append(slice, 4, 5)
fmt.Println("Slice:", slice) // خروجی: [1 2 3 4 5]
fmt.Println("Length:", len(slice)) // 5
fmt.Println("Capacity:", cap(slice)) // 5 یا بیشتر
subSlice := slice[1:4]
fmt.Println("Subslice:", subSlice) // [2 3 4]
// تغییر در subSlice روی slice اصلی اثر میگذارد
subSlice[0] = 99
fmt.Println("Original after change:", slice) // [1 99 3 4 5]
}
append
یک آرایه جدید با ظرفیت بیشتر (معمولاً دو برابر) ایجاد میکند.var s1 []int // nil slice
s2 := []int{} // اسلایس خالی
fmt.Println(s1 == nil) // true
fmt.Println(s2 == nil) // false
append(slice, 1)
به تنهایی تأثیری ندارد).مپ یک دادهساختار کلید-مقدار است که برای ذخیره و بازیابی سریع دادهها استفاده میشود.
var m map[string]int // مپ خالی (nil)
m := make(map[string]int) // مپ آماده
m := map[string]int{"Ali": 30, "Bob": 25} // مقداردهی مستقیم
m["Ali"] = 30
delete(m, "Ali")
value, exists := m["Ali"]
package main
import "fmt"
func main() {
m := make(map[string]int)
m["Ali"] = 30
m["Bob"] = 25
fmt.Println("Map:", m) // خروجی: map[Ali:30 Bob:25]
age, exists := m["Ali"]
if exists {
fmt.Println("Age of Ali:", age) // 30
}
delete(m, "Bob")
fmt.Println("After delete:", m) // map[Ali:30]
}
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
sort.Strings(keys)
sync.RWMutex
استفاده کنید.رشتهها در Go دنبالهای از بایتها هستند که معمولاً به صورت UTF-8 ذخیره میشوند. نوع rune
برای نمایش کاراکترهای یونیکد استفاده میشود (معادل int32
).
s := "Hello, جهان"
r := rune('ج') // یک کاراکتر یونیکد
s := "Hello"
fmt.Println(s[0]) // 72 (بایت H)
for i, r := range "جهان" {
fmt.Printf("Index: %d, Rune: %c\n", i, r)
}
s := "Hello" + " World"
package main
import (
"fmt"
"unicode/utf8"
)
func main() {
s := "Hello, جهان"
fmt.Println("Length in bytes:", len(s)) // تعداد بایتها
fmt.Println("Length in runes:", utf8.RuneCountInString(s)) // تعداد کاراکترها
for i, r := range s {
fmt.Printf("Index: %d, Rune: %c\n", i, r)
}
// تبدیل به rune
runes := []rune(s)
fmt.Println("First rune:", string(runes[0])) // H
}
[]rune
تبدیل کنید.strings.Builder
استفاده کنید:
var builder strings.Builder
for i := 0; i < 1000; i++ {
builder.WriteString("x")
}
result := builder.String()
len
برای شمارش کاراکترها (بایتها را میشمارد).ساختارها برای تعریف دادههای پیچیده با چندین فیلد استفاده میشوند.
type Person struct {
Name string
Age int
}
p := Person{Name: "Ali", Age: 30}
fmt.Println(p.Name) // Ali
func (p Person) Greet() string {
return "Hello, " + p.Name
}
package main
import "fmt"
type Person struct {
Name string
Age int
}
func (p Person) Greet() string {
return fmt.Sprintf("Hello, %s! You are %d years old.", p.Name, p.Age)
}
func main() {
p := Person{Name: "Ali", Age: 30}
fmt.Println(p.Greet()) // Hello, Ali! You are 30 years old.
// اشارهگر به struct
pp := &p
pp.Age = 31
fmt.Println(p.Age) // 31
}
type Employee struct {
Person
ID int
}
e := Employee{Person: Person{Name: "Ali"}, ID: 123}
fmt.Println(e.Name) // Ali
func (p *Person) Birthday() {
p.Age++
}
رابطها مجموعهای از متدها را تعریف میکنند که یک نوع باید پیادهسازی کند.
type Shape interface {
Area() float64
}
هر نوع که متدهای رابط را پیادهسازی کند، به طور خودکار آن رابط را ارضا میکند.
package main
import (
"fmt"
"math"
)
type Shape interface {
Area() float64
}
type Circle struct {
Radius float64
}
func (c Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}
type Rectangle struct {
Width, Height float64
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
func printArea(s Shape) {
fmt.Printf("Area: %.2f\n", s.Area())
}
func main() {
c := Circle{Radius: 5}
r := Rectangle{Width: 4, Height: 6}
printArea(c) // Area: 78.54
printArea(r) // Area: 24.00
}
interface{}
): میتواند هر مقداری را نگه دارد (مشابه any
در Go 1.18+).
var i interface{}
i = 42
i = "hello"
if v, ok := i.(string); ok {
fmt.Println("String:", v)
}
switch v := i.(type) {
case int:
fmt.Println("Int:", v)
case string:
fmt.Println("String:", v)
}
Go دارای سیستم مدیریت حافظه خودکار (garbage collection) است، اما درک اشارهگرها و بهینهسازی حافظه برای نوشتن برنامههای کارآمد ضروری است.
اشارهگر متغیری است که آدرس حافظه یک متغیر دیگر را ذخیره میکند.
var p *int // اشارهگر به int
x := 42
p = &x // آدرس x
*p = 43 // تغییر مقدار
package main
import "fmt"
func increment(p *int) {
*p++
}
func main() {
x := 42
increment(&x)
fmt.Println(x) // 43
}
p++
).Go از یک garbage collector (GC) برای مدیریت حافظه استفاده میکند که اشیاء غیرقابل دسترس را آزاد میکند.
var pool = sync.Pool{
New: func() interface{} {
return &bytes.Buffer{}
},
}
func main() {
buf := pool.Get().(*bytes.Buffer)
defer pool.Put(buf)
buf.WriteString("test")
}
pprof
برای بررسی مصرف حافظه استفاده کنید:
go tool pprof http://localhost:6060/debug/pprof/heap
GOGC
برای تنظیم رفتار GC:
GOGC=200 go run main.go
strings.Builder
برای اتصال رشتهها استفاده کنید.package main
import (
"fmt"
"strings"
)
func main() {
var builder strings.Builder
for i := 0; i < 1000; i++ {
builder.WriteString("x")
}
fmt.Println("Length:", builder.Len())
}
go build -gcflags="-m"
func BenchmarkSliceAppend(b *testing.B) {
s := []int{}
for i := 0; i < b.N; i++ {
s = append(s, i)
}
}
این جزوه دادهساختارهای اصلی Go (آرایهها، اسلایسها، مپها، رشتهها، ساختارها، و رابطها) و مدیریت حافظه (اشارهگرها، garbage collection، و بهینهسازی) را با جزئیات کامل پوشش داد. هر بخش با مثالهای عملی، نکات پیشرفته، و خطاهای رایج همراه بود تا درک عمیقی از این مفاهیم فراهم شود.
برای یادگیری بیشتر:
https://golang.org/doc/