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)
    }
}

Run in playground

Đ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)
}

Run in playground

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)
}

Run in playground

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])
}

Run in playground

Ở 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 int0 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"])
}

Run in playground

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")
    }

}

Run in playground

Ở 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)
    }

}

Run in playground

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)

}

Run in playground

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))

}

Run in playground

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)

}

Run in playground

Ở 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 {
    }
}

Run in playground

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