Chào mừng các bạn đến với bài hướng dẫn thứ 13 của series hướng dẫn lập trình Golang cơ bản.
Map là gì?
Map (bản đồ) là một kiểu dữ liệu được dựng sẵn trong Go, một map là tập hợp các cặp key/value (khóa/giá trị) trong đó một value được liên kết với một key. Value chỉ được truy xuất bởi key tương ứng.
Khởi tạo map
Một map được khởi tạo bằng cách truyền kiểu dữ liệu của key và value thông qua hàm make. Cú pháp như sau: make(map[type of key]type of value)
personSalary := make(map[string]int)
Dòng code trên khởi tạo một map có tên là personSalary với các key kiểu string và value kiểu int.
Giá trị rỗng của map được gọi là nil. Nếu ta cố gắng thêm các item (phần tử) vào một map nil thì run time panic sẽ xảy ra. Do đó nếu ta có một map là nil thì nó phải được khởi tạo và cấp phát vùng nhớ bằng hàm make.
package main
import (
"fmt"
)
func main() {
var personSalary map[string]int // cách khai báo thông thường, map sẽ là nil
if personSalary == nil {
fmt.Println("map is nil. Going to make one.")
personSalary = make(map[string]int)
}
}
Đoạn code trên, theo cách khai báo thông thường như vậy thì map personSalary sẽ là nil, vì thế ta sẽ phải khởi tạo map personSalary bằng hàm make. Output của chương trình trên là map is nil. Going to make one.
Thêm phần tử vào map
Cú pháp để thêm phần tử mới vào map cũng tương tự như với array. Chương trình dưới đây thêm một số phần tử mới vào map personSalary.
package main
import (
"fmt"
)
func main() {
personSalary := make(map[string]int)
personSalary["steve"] = 12000
personSalary["jamie"] = 15000
personSalary["mike"] = 9000
fmt.Println("personSalary map contents:", personSalary)
}
Output của chương trình: personSalary map contents: map[steve:12000 jamie:15000 mike:9000]
Ta cũng có thể vừa khởi tạo map kèm thêm khai báo phần tử vào luôn:
package main
import (
"fmt"
)
func main() {
personSalary := map[string]int {
"steve": 12000,
"jamie": 15000,
}
personSalary["mike"] = 9000
fmt.Println("personSalary map contents:", personSalary)
}
Chương trình trên khai báo map personSalary và thêm 2 phần tử vào ngay trong quá trình khai báo. Sau đó thêm 1 phần tử nữa có key là mike. Output của chương trình:
personSalary map contents: map[steve:12000 jamie:15000 mike:9000]
Không chỉ kiểu string mà tất cả những kiểu dữ liệu có thể so sánh như boolean, integer, float, complex,… đều có thể làm key. Bạn có thể tìm hiểu kỹ hơn về các kiểu dữ liệu có thể so sánh tại: http://golang.org/ref/spec#Comparison_operators
Truy cập các phần tử của map
Ta đã thêm một số phần tử vào map, giờ hãy tìm hiểu cách truy xuất chúng. Cú pháp để truy xuất phần tử của map: map[key]
package main
import (
"fmt"
)
func main() {
personSalary := map[string]int{
"steve": 12000,
"jamie": 15000,
}
personSalary["mike"] = 9000
employee := "jamie"
fmt.Println("Salary of", employee, "is", personSalary[employee])
}
Ở chương trình trên, tiền lương của nhân viên jamie được truy xuất và in ra. Ta có output: Salary of jamie is 15000.
Nếu một phần tử không tồn tại thì sao? Map sẽ trả về giá trị 0 tương ứng với kiểu dữ liệu của phần tử đó. Trong trường hợp map personSalary này, nếu ta truy cập đến một phẩn tử không tồn tại, thì giá trị 0 của kiểu int là 0 sẽ được trả về.
package main
import (
"fmt"
)
func main() {
personSalary := map[string]int{
"steve": 12000,
"jamie": 15000,
}
personSalary["mike"] = 9000
employee := "jamie"
fmt.Println("Salary of", employee, "is", personSalary[employee])
fmt.Println("Salary of joe is", personSalary["joe"])
}
Output của chương trình:
Salary of jamie is 15000
Salary of joe is 0
Chương trình trên trả về Salary of joe là 0. Ta không nhận được bất kỳ runtime error nào nói rằng key joe không tồn tại trong map personSalary.
Nếu chúng ta muốn biết một key có tốn tại trong map hay không thì phải làm thế nào?
value, ok := map[key]
Cú pháp trên dùng để tìm xem một key cụ thể có tồn tại trong map hay không. Nếu ok là đúng, thì key là có tồn tại và value của nó cũng có giá trị, và ngược lại.
package main
import (
"fmt"
)
func main() {
personSalary := map[string]int{
"steve": 12000,
"jamie": 15000,
}
personSalary["mike"] = 9000
newEmp := "joe"
value, ok := personSalary[newEmp]
if ok == true {
fmt.Println("Salary of", newEmp, "is", value)
} else {
fmt.Println(newEmp,"not found")
}
}
Ở chương trình trên ok là false vì joe không tồn tại, ta có output sau:
joe not found
Dạng range của vòng lặp for được sử dụng để duyệt qua tất cả các phần tử của map.
package main
import (
"fmt"
)
func main() {
personSalary := map[string]int{
"steve": 12000,
"jamie": 15000,
}
personSalary["mike"] = 9000
fmt.Println("All items of a map")
for key, value := range personSalary {
fmt.Printf("personSalary[%s] = %d\n", key, value)
}
}
Output của chương trình trên:
All items of a map
personSalary[jamie] = 15000
personSalary[mike] = 9000
personSalary[steve] = 12000
Chúng ta để ý thứ tự truy xuất các value từ một map khi sử dụng for range có thể không giống nhau với mỗi lần thực hiện chương trình.
Xóa phần tử
delete(map, key) là cú pháp để xóa 1 key khỏi map. Hàm delete này không trả về bất kỳ giá trị nào.
package main
import (
"fmt"
)
func main() {
personSalary := map[string]int{
"steve": 12000,
"jamie": 15000,
}
personSalary["mike"] = 9000
fmt.Println("map before deletion", personSalary)
delete(personSalary, "steve")
fmt.Println("map after deletion", personSalary)
}
Chương trình trên xóa key "steve" khỏi map personSalary và output là:
map before deletion map[steve:12000 jamie:15000 mike:9000]
map after deletion map[mike:9000 jamie:15000]
Chiều dài của map
Chiều dài của một map có thể được xác định bằng hàm len.
package main
import (
"fmt"
)
func main() {
personSalary := map[string]int{
"steve": 12000,
"jamie": 15000,
}
personSalary["mike"] = 9000
fmt.Println("length is", len(personSalary))
}
len(personSalary) trong chương trình trên xác định độ dài của map personSalary. Ta có output: length is 3
Map là kiểu tham chiếu
Tương tự như slice, map là kiểu tham chiếu. Khi một map được gán cho một biến mới, cả hai đều trỏ đến cùng một cấu trúc dữ liệu nội bộ. Do đó những thay đổi được thực hiện ở một biến sẽ ánh xạ đến biến kia.
package main
import (
"fmt"
)
func main() {
personSalary := map[string]int{
"steve": 12000,
"jamie": 15000,
}
personSalary["mike"] = 9000
fmt.Println("Original person salary", personSalary)
newPersonSalary := personSalary
newPersonSalary["mike"] = 18000
fmt.Println("Person salary changed", personSalary)
}
Ở chương trình trên ta thấy map personSalary được gán cho biến mới là newPersonSalary. Dòng tiếp theo, tại map newPersonSalary, salary của mike được thay đổi thành 18000. Và salary của mike cũng được thay đổi thành 18000 tại map personSalary. Output của chương trình:
Original person salary map[steve:12000 jamie:15000 mike:9000]
Person salary changed map[steve:12000 jamie:15000 mike:18000]
Trường hợp khi map là tham số truyền vào của một hàm thì cũng tương tự như vậy. Khi có bất kỳ sự thay đổi nào được thực hiện đối với map ở bên trong hàm đó, thì map sẽ thực sự thay đổi và về sau khi gọi đến map này chúng ta sẽ thấy sự thay đổi đó.
Map bằng nhau
Map không thể so sánh với nhau bằng toán tử ==. Ta chỉ có thể sử dụng == để kiểm tra xem một map có phải là nil hay không.
package main
func main() {
map1 := map[string]int{
"one": 1,
"two": 2,
}
map2 := map1
if map1 == map2 {
}
}
Chương trình trên khi chạy sẽ ném ra lỗi biên dịch: invalid operation: map1 == map2 (map can only be compared to nil).
Một cách để kiểm tra xem hai map có bằng nhau hay không là so sánh từng phần tử riêng lẻ với nhau. Tôi khuyến khích các bạn viết một chương trình để làm điều đó :).
Tôi đã biên soạn tất cả các nội dung mà chúng ta vừa thảo luận trong một chương trình nhỏ. Bạn có thể tải xuống từ github.
Đó là tất cả những gì cơ bản về map. Cám ơn các bạn đã đọc.
Phần tiếp theo - Strings
Bài viết được dịch từ golangbot
Bình luận