Mục lục
I. Cấu trúc dữ liệu (Struct)
-
Khai báo cấu trúc
-
Truy cập thành phần của cấu trúc
-
Truyền cấu trúc vào hàm
-
Con trỏ đến cấu trúc
II. Slice trong Go
-
Khởi tạo slice
-
Hàm len() và cap()
-
Slice rỗng (nil)
-
Cắt slice
-
Hàm append() và copy()
I. Cấu trúc dữ liệu (Struct)
1. Khai báo cấu trúc
Trong Go, mảng chỉ có thể chứa các phần tử cùng kiểu dữ liệu, nhưng với cấu trúc bạn có thể định nghĩa các kiểu dữ liệu khác nhau cho từng thành phần.
Cấu trúc là một tập hợp các dữ liệu có thể cùng hoặc khác kiểu, được nhóm lại thành một đơn vị.
Để khai báo cấu trúc, bạn sử dụng từ khóa type và struct. Câu lệnh struct định nghĩa kiểu dữ liệu mới, trong đó có một hoặc nhiều thành phần. Từ khóa type đặt tên cho cấu trúc.
package main
import "fmt"
// Khai báo cấu trúc
type Books struct {
title string
author string
subject string
bookId int
}
func main() {
// Tạo một cấu trúc mới
fmt.Println(Books{"Ngôn ngữ Go", "mr.white", "Hướng dẫn Go", 100001})
// Có thể dùng cú pháp key => value
fmt.Println(Books{title: "Ngôn ngữ Go", author: "mr.white", subject: "Hướng dẫn Go", bookId: 100002})
// Các trường bị bỏ qua sẽ có giá trị mặc định
fmt.Println(Books{title: "Ngôn ngữ Go", author: "mr.white"})
}
2. Truy cập thành phần của cấu trúc
Để truy cập thành phần trong cấu trúc, bạn dùng toán tử dấu chấm ..
package main
import "fmt"
// Khai báo cấu trúc
type Books struct {
title string
author string
subject string
bookId int
}
func main() {
var Book1, Book2 Books
/* Thông tin về sách 1 */
Book1.title = "Ngôn ngữ Go"
Book1.author = "mr.white"
Book1.subject = "Hướng dẫn ngôn ngữ Go"
Book1.bookId = 100001
/* Thông tin về sách 2 */
Book2.title = "Hướng dẫn Python"
Book2.author = "mr.white"
Book2.subject = "Hướng dẫn ngôn ngữ Python"
Book2.bookId = 100002
/* In thông tin sách 1 */
fmt.Printf("Tiêu đề sách 1: %s\n", Book1.title)
fmt.Printf("Tác giả sách 1: %s\n", Book1.author)
fmt.Printf("Chủ đề sách 1: %s\n", Book1.subject)
fmt.Printf("ID sách 1: %d\n", Book1.bookId)
/* In thông tin sách 2 */
fmt.Printf("Tiêu đề sách 2: %s\n", Book2.title)
fmt.Printf("Tác giả sách 2: %s\n", Book2.author)
fmt.Printf("Chủ đề sách 2: %s\n", Book2.subject)
fmt.Printf("ID sách 2: %d\n", Book2.bookId)
}
3. Truyền cấu trúc vào hàm
Trong Go, bạn có thể truyền cấu trúc vào hàm dưới dạng giá trị hoặc con trỏ.
Khi truyền giá trị cấu trúc vào hàm, bạn đang truyền bản sao của nó. Những thay đổi bên trong hàm sẽ không ảnh hưởng đến cấu trúc gốc.
package main
import "fmt"
// Định nghĩa cấu trúc
type Person struct {
Name string
Age int
}
// Truyền giá trị cấu trúc
func updateAge(p Person) {
p.Age = 30
fmt.Println("Trong hàm updateAge:", p)
}
func main() {
person := Person{Name: "Alice", Age: 25}
updateAge(person)
fmt.Println("Sau khi gọi hàm:", person)
}
4. Con trỏ đến cấu trúc
Khi truyền con trỏ cấu trúc vào hàm, bạn đang làm việc trực tiếp với cấu trúc gốc. Mọi thay đổi đều ảnh hưởng đến cấu trúc ban đầu.
package main
import "fmt"
// Định nghĩa cấu trúc
type Person struct {
Name string
Age int
}
// Truyền con trỏ cấu trúc
func updateAge(p *Person) {
p.Age = 30
fmt.Println("Trong hàm updateAge:", *p)
}
func main() {
person := Person{Name: "Alice", Age: 25}
updateAge(&person)
fmt.Println("Sau khi gọi hàm:", person)
}
Khi chọn giữa truyền giá trị hay con trỏ, cần cân nhắc:
- Nếu cấu trúc lớn, dùng con trỏ để tiết kiệm bộ nhớ.
- Nếu muốn thay đổi cấu trúc trong hàm, phải dùng con trỏ.
- Nếu chỉ đọc dữ liệu, truyền giá trị an toàn hơn.
Thông thường trong Go, nên dùng con trỏ để truyền cấu trúc, giúp tiết kiệm tài nguyên và tránh sao chép không cần thiết.
II. Slice trong Go
1. Khởi tạo slice
Mảng trong Go có độ dài cố định. Slice (mảng động) cho phép thêm phần tử linh hoạt, có thể mở rộng khi cần.
package main
import "fmt"
func main() {
// Khởi tạo slice trực tiếp
s := []int{1, 2, 3}
fmt.Println("Slice s:", s)
// Tạo slice từ mảng
s1 := s[:]
fmt.Println("Slice s1:", s1)
// Cắt slice từ chỉ số 1 đến 3
s2 := s[1:3]
fmt.Println("Slice s2:", s2)
// Dùng make để khởi tạo
s3 := make([]int, 3)
fmt.Println("Slice s3:", s3)
}
2. Hàm len() và cap()
len()trả về số phần tử trong slice.cap()trả về sức chứa tối đa của slice.
package main
import "fmt"
func showSlice(x []int) {
fmt.Printf("len=%d cap=%d slice=%v\n", len(x), cap(x), x)
}
func main() {
// Khởi tạo slice với độ dài 3, sức chứa 5
var numbers = make([]int, 3, 5)
showSlice(numbers)
}
3. Slice rỗng (nil)
package main
import "fmt"
func showSlice(x []int) {
fmt.Printf("len=%d cap=%d slice=%v\n", len(x), cap(x), x)
}
func main() {
// Khởi tạo slice rỗng
var numbers []int
showSlice(numbers)
if numbers == nil {
fmt.Println("Slice là rỗng")
}
}
4. Cắt slice
package main
import "fmt"
func showSlice(x []int) {
fmt.Printf("len=%d cap=%d slice=%v\n", len(x), cap(x), x)
}
func main() {
// Tạo slice
numbers := []int{0, 1, 2, 3, 4, 5, 6, 7, 8}
showSlice(numbers)
// Cắt slice từ index 1 đến 4
fmt.Println("numbers[1:4] =", numbers[1:4])
// Mặc định từ đầu đến index 3
fmt.Println("numbers[:3] =", numbers[:3])
// Mặc định từ index 4 đến cuối
fmt.Println("numbers[4:] =", numbers[4:])
// Tạo slice rỗng với sức chứa 5
numbers1 := make([]int, 0, 5)
showSlice(numbers1)
// Cắt từ đầu đến index 2
number2 := numbers[:2]
showSlice(number2)
// Cắt từ index 2 đến 5
number3 := numbers[2:5]
showSlice(number3)
}
5. Hàm append() và copy()
Để tăng kích thước slice, bạn phải tạo một slice mới và sao chép nội dung cũ.
package main
import "fmt"
func showSlice(x []int) {
fmt.Printf("len=%d cap=%d slice=%v\n", len(x), cap(x), x)
}
func main() {
var numbers []int
showSlice(numbers)
// Thêm phần tử vào slice rỗng
numbers = append(numbers, 0)
showSlice(numbers)
// Thêm một phần tử
numbers = append(numbers, 1)
showSlice(numbers)
// Thêm nhiều phần tử cùng lúc
numbers = append(numbers, 2, 3, 4)
showSlice(numbers)
// Tạo slice mới với sức chứa gấp đôi
numbers1 := make([]int, len(numbers), cap(numbers)*2)
// Sao chép dữ liệu từ slice cũ sang mới
copy(numbers1, numbers)
showSlice(numbers1)
}