Chuyển tới nội dung chính

Mảng và Slice trong Go - Cách lưu trữ dữ liệu thông minh! 📦

Chào mừng bạn đến với bài học về mảng và slice trong Go! Giống như một hộp đựng đồ, mảng và slice giúp chúng ta lưu trữ nhiều thứ cùng một lúc. Trong bài học này, chúng ta sẽ tìm hiểu cách sử dụng chúng một cách hiệu quả.

Mảng là gì? 🤔

Giống như một hộp có số ngăn cố định:

  • Có kích thước cố định (không thể thêm/bớt ngăn)
  • Mỗi ngăn chứa một thứ cùng loại
  • Đánh số từ 0 đến n-1

1. Khai báo mảng (Tạo hộp mới) 📝

// Tạo hộp có 5 ngăn cho số
var numbers [5]int

// Tạo hộp có 3 ngăn cho tên
var names [3]string

// Tạo hộp và đặt đồ vào ngay
numbers := [5]int{1, 2, 3, 4, 5}
names := [3]string{"John", "Jane", "Bob"}

💡 Giải thích:

  • [5]int: Hộp có 5 ngăn, mỗi ngăn chứa số nguyên
  • {1, 2, 3, 4, 5}: Đặt các số vào từng ngăn

2. Truy cập phần tử (Lấy đồ từ ngăn) 🎯

// Lấy số ở ngăn đầu tiên
firstNumber := numbers[0]

// Lấy số ở ngăn cuối cùng
lastNumber := numbers[len(numbers)-1]

// Đặt số mới vào ngăn đầu tiên
numbers[0] = 10

💡 Giải thích:

  • numbers[0]: Lấy đồ ở ngăn số 0
  • len(numbers): Đếm số ngăn trong hộp

3. Duyệt qua mảng (Xem tất cả đồ trong hộp) 🔄

// Cách 1: Đếm từng ngăn
for i := 0; i < len(numbers); i++ {
fmt.Println(numbers[i])
}

// Cách 2: Dùng for range (dễ hơn)
for index, value := range numbers {
fmt.Printf("Ngăn %d: %d\n", index, value)
}

💡 Giải thích:

  • for range: Tự động đếm từng ngăn
  • index: Số ngăn
  • value: Đồ trong ngăn

Slice là gì? 🤔

Giống như một hộp thông minh:

  • Có thể thêm/bớt ngăn
  • Vẫn giữ đồ theo thứ tự
  • Dễ dàng thay đổi kích thước

1. Khai báo slice (Tạo hộp thông minh) 📝

// Tạo hộp rỗng
var numbers []int
var names []string

// Tạo hộp và đặt đồ vào ngay
numbers := []int{1, 2, 3, 4, 5}
names := []string{"John", "Jane", "Bob"}

💡 Giải thích:

  • []int: Hộp thông minh chứa số
  • Không cần chỉ định số ngăn

2. Tạo slice từ mảng (Chia hộp cũ) 🔄

// Tạo hộp cũ
arr := [5]int{1, 2, 3, 4, 5}

// Lấy một phần của hộp
slice := arr[1:4] // [2, 3, 4]

💡 Giải thích:

  • [1:4]: Lấy từ ngăn 1 đến ngăn 3
  • Không bao gồm ngăn 4

3. Thêm phần tử (Thêm ngăn mới) ➕

numbers := []int{1, 2, 3}
numbers = append(numbers, 4, 5)

💡 Giải thích:

  • append: Thêm ngăn mới
  • Có thể thêm nhiều ngăn cùng lúc

4. Tạo slice với make (Tạo hộp có kế hoạch) 🏗️

// Tạo hộp có 3 ngăn, có thể mở rộng đến 5 ngăn
numbers := make([]int, 3, 5)

💡 Giải thích:

  • 3: Số ngăn ban đầu
  • 5: Số ngăn tối đa có thể có

So sánh Mảng và Slice 🔍

Mảng (Hộp cố định)

  • ✅ Kích thước cố định
  • ✅ Có thể so sánh trực tiếp
  • ❌ Không thể thay đổi kích thước
  • ❌ Không linh hoạt

Slice (Hộp thông minh)

  • ✅ Kích thước động
  • ✅ Dễ dàng thay đổi
  • ❌ Không thể so sánh trực tiếp
  • ✅ Rất linh hoạt

Ví dụ thực tế 🎯

1. Xử lý dữ liệu (Lọc số dương)

func processData(data []int) []int {
result := make([]int, 0)
for _, v := range data {
if v > 0 {
result = append(result, v)
}
}
return result
}

💡 Giải thích:

  • Tạo hộp mới để chứa số dương
  • Kiểm tra từng số và thêm vào nếu là số dương

2. Tìm kiếm (Tìm vị trí của số)

func findIndex(slice []int, target int) int {
for i, v := range slice {
if v == target {
return i
}
}
return -1
}

💡 Giải thích:

  • Duyệt qua từng ngăn
  • Trả về vị trí nếu tìm thấy
  • Trả về -1 nếu không tìm thấy

3. Sắp xếp (Xếp đồ theo thứ tự)

func sortSlice(slice []int) {
sort.Ints(slice)
}

💡 Giải thích:

  • Sử dụng hàm có sẵn để sắp xếp
  • Sắp xếp theo thứ tự tăng dần

Best Practices (Cách sử dụng tốt) 🌟

1. Chọn đúng loại hộp

// ✅ Tốt: Dùng slice khi cần linh hoạt
numbers := []int{1, 2, 3}

// ❌ Không tốt: Dùng mảng khi không cần thiết
numbers := [3]int{1, 2, 3}

💡 Lý do: Slice linh hoạt hơn mảng

2. Khởi tạo slice thông minh

// ✅ Tốt: Chỉ định sức chứa
numbers := make([]int, 0, 100)

// ❌ Không tốt: Không chỉ định sức chứa
numbers := []int{}

💡 Lý do: Tránh phải mở rộng hộp nhiều lần

3. Xử lý nil slice an toàn

// ✅ Tốt: Kiểm tra nil
if slice != nil {
// Xử lý slice
}

// ❌ Không tốt: Truy cập trực tiếp
slice[0] = 1 // Có thể gây lỗi

💡 Lý do: Tránh lỗi khi slice rỗng

Những lỗi thường gặp và cách sửa 🔧

  1. Lỗi: Truy cập ngoài phạm vi

    // ❌ Sai
    numbers := []int{1, 2, 3}
    fmt.Println(numbers[5]) // Lỗi!

    // ✅ Đúng
    if 5 < len(numbers) {
    fmt.Println(numbers[5])
    }
  2. Lỗi: Quên khởi tạo slice

    // ❌ Sai
    var numbers []int
    numbers[0] = 1 // Lỗi!

    // ✅ Đúng
    numbers := make([]int, 1)
    numbers[0] = 1
  3. Lỗi: Copy slice không cần thiết

    // ❌ Sai
    newSlice := make([]int, len(oldSlice))
    copy(newSlice, oldSlice) // Không cần thiết

    // ✅ Đúng
    newSlice := oldSlice

Tiếp theo 🎯

Trong bài học tiếp theo, chúng ta sẽ:

  • Tìm hiểu về maps (bản đồ dữ liệu)
  • Học cách sử dụng structs (cấu trúc dữ liệu)
  • Khám phá cách xử lý JSON

💡 Lời khuyên: Hãy thử tạo và thao tác với các slice đơn giản để hiểu rõ hơn về cách chúng hoạt động!