Mảng trong Go

Trong hướng dẫn này, chúng ta sẽ tìm hiểu mảng (array) là gì? cách khai báo mảng một chiều, mảng đa chiều, duyệt mảng sử dụng vòng lặp for và range trong Go.

Mảng là gì?

Mảng (array) là một tập hợp các phần tử thuộc cùng một kiểu dữ liệu. Ví dụ, tập hợp các số nguyên 5, 8, 9, 79, 76 tạo thành một mảng. Việc trộn các giá trị của các kiểu khác nhau, chẳng hạn như một mảng chứa cả chuỗi và số nguyên không được phép trong Go.

Khai báo mảng

Một mảng thuộc về kiểu [n]T. n biểu thị số phần tử trong một mảng và T đại diện cho kiểu dữ liệu của mỗi phần tử. Số lượng phần tử n cũng là một phần của kiểu (Chúng ta sẽ thảo luận chi tiết hơn về vấn đề này ngay sau đây).

Có nhiều cách khác nhau để khai báo mảng. Hãy xem xét từng cái một.

package main

import (  
    "fmt"
)


func main() {  
    var a [3]int //int array with length 3
    fmt.Println(a)
}

Chạy chương trình trong playground

var a [3]int khai báo một mảng số nguyên có độ dài là 3. Tất cả các phần tử trong một mảng được tự động gán zero value của kiểu mảng. Trong trường hợp này a là một mảng số nguyên và do đó tất cả các phần tử của a được gán giá trị 0. Chạy chương trình trên sẽ in ra kết quả sau:

[0 0 0]

Chỉ mục của một mảng bắt đầu từ 0 và kết thúc tại length - 1. Hãy gán một số giá trị cho mảng trên.

package main

import (  
    "fmt"
)


func main() {  
    var a [3]int //int array with length 3
    a[0] = 12 // array index starts at 0
    a[1] = 78
    a[2] = 50
    fmt.Println(a)
}

Chạy chương trình trong playground

a[0] gán giá trị cho phần tử đầu tiên của mảng. Chương trình sẽ in ra kết quả sau:

[12 78 50]

Hãy tạo cùng một mảng bằng cách sử dụng khai báo rút gọn.

package main 

import (  
    "fmt"
)

func main() {  
    a := [3]int{12, 78, 50} // short hand declaration to create array
    fmt.Println(a)
}

Chạy chương trình trong playground

Chương trình trên sẽ in ra cùng một kết quả:

[12 78 50]

Không nhất thiết tất cả các phần tử trong một mảng phải được gán một giá trị trong quá trình khai báo ngắn gọn.

package main

import (  
    "fmt"
)

func main() {  
    a := [3]int{12} 
    fmt.Println(a)
}

Chạy chương trình trong playground

Trong chương trình trên, dòng số 8 a := [3]int{12} khai báo một mảng có độ dài 3 nhưng chỉ phần tử đầu tiên được cung cấp một giá trị 12. Hai phần tử còn lại được gán giá trị 0 tự động. Chương trình này sẽ in ra kết quả sau:

[12 0 0]

Bạn thậm chí có thể bỏ qua độ dài của mảng trong khai báo và thay thế bằng ... và để trình biên dịch tìm độ dài cho bạn. Điều này được thực hiện trong chương trình sau.

package main

import (  
    "fmt"
)

func main() {  
    a := [...]int{12, 78, 50} // ... makes the compiler determine the length
    fmt.Println(a)
}

Chạy chương trình trong playground

Kích thước của mảng là một phần của kiểu. Do đó [5]int[25]int là các kiểu khác nhau. Do đó, các mảng không thể được thay đổi kích thước. Đừng lo lắng về hạn chế này vì slices tồn tại để khắc phục điều này.

package main

func main() {  
    a := [3]int{5, 78, 8}
    var b [5]int
    b = a //not possible since [3]int and [5]int are distinct types
}

Chạy chương trình trong playground

Tại dòng số 6 của chương trình trên, chúng ta đang cố gắng gán một biến kiểu [3]int cho một biến kiểu [5]int. Điều này là không được phép và do đó trình biên dịch sẽ in ra lỗi sau:

./prog.go:6:7: cannot use a (type [3]int) as type [5]int in assignment

Mảng là kiểu giá trị

Mảng trong Go là kiểu giá trị, không phải kiểu tham chiếu. Điều này có nghĩa là khi chúng được gán cho một biến mới, một bản sao của mảng ban đầu sẽ được gán cho biến mới. Nếu các thay đổi được thực hiện đối với biến mới, nó sẽ không được phản ánh trong mảng ban đầu.

package main

import "fmt"

func main() {  
    a := [...]string{"USA", "China", "India", "Germany", "France"}
    b := a // a copy of a is assigned to b
    b[0] = "Singapore"
    fmt.Println("a is ", a)
    fmt.Println("b is ", b) 
}

Chạy chương trình trong playground

Trong chương trình trên, tại dòng số 7, một bản sao của biến a được gán cho biến b. Tại dòng số 8, phần tử đầu tiên của b được thay đổi thành Singapore. Điều này sẽ không làm thay đổi mảng ban đầu a. Chương trình sẽ in ra kết quả sau:

a is [USA China India Germany France]  
b is [Singapore China India Germany France]  

Tương tự như vậy khi mảng được truyền cho các hàm dưới dạng tham số, chúng được truyền theo giá trị và mảng ban đầu không thay đổi.

package main

import "fmt"

func changeLocal(num [5]int) {  
    num[0] = 55
    fmt.Println("inside function ", num)

}
func main() {  
    num := [...]int{5, 6, 7, 8, 8}
    fmt.Println("before passing to function ", num)
    changeLocal(num) //num is passed by value
    fmt.Println("after passing to function ", num)
}

Chạy chương trình trong playground

Trong chương trình trên, tại dòng số 13, mảng num thực sự truyền giá trị cho hàm changeLocal và do đó sẽ không thay đổi do lệnh gọi hàm. Chương trình này sẽ in ra kết quả sau:

before passing to function  [5 6 7 8 8]  
inside function  [55 6 7 8 8]  
after passing to function  [5 6 7 8 8]  

Chiều dài của một mảng

Độ dài của mảng được tìm thấy bằng cách truyền mảng dưới dạng tham số cho function len.

package main

import "fmt"

func main() {  
    a := [...]float64{67.7, 89.8, 21, 78}
    fmt.Println("length of a is",len(a))

}

Chạy chương trình trong sân chơi

Kết quả của chương trình trên là:

length of a is 4  

Duyệt mảng sử dụng range

Vòng lặp for có thể được sử dụng để duyệt qua các phần tử của một mảng.

package main

import "fmt"

func main() {  
    a := [...]float64{67.7, 89.8, 21, 78}
    for i := 0; i < len(a); i++ { //looping from 0 to the length of the array
        fmt.Printf("%d th element of a is %.2f\n", i, a[i])
    }
}

Chạy chương trình trong playground

Chương trình trên sử dụng một vòng lặp for để lặp qua các phần tử của mảng bắt đầu từ chỉ mục 0 đến length of the array - 1. Chương trình này hoạt động và sẽ in ra kết quả sau:

0 th element of a is 67.70  
1 th element of a is 89.80  
2 th element of a is 21.00  
3 th element of a is 78.00  

Go cung cấp một cách tốt hơn và ngắn gọn để duyệt qua một mảng bằng cách sử dụng range của vòng lặp for. range trả về cả chỉ mục và giá trị tại chỉ mục đó. Hãy viết lại đoạn mã trên bằng cách sử dụng range. Chúng ta cũng sẽ tìm tổng của tất cả các phần tử của mảng.

package main

import "fmt"

func main() {  
    a := [...]float64{67.7, 89.8, 21, 78}
    sum := float64(0)
    for i, v := range a {//range returns both the index and value
        fmt.Printf("%d the element of a is %.2f\n", i, v)
        sum += v
    }
    fmt.Println("\nsum of all elements of a",sum)
}

Chạy chương trình trong playground

Dòng số 8 for i, v := range a của chương trình trên là range của vòng lặp for. Nó sẽ trả về cả chỉ mục và giá trị tại chỉ mục đó. Chúng ta in các giá trị và cũng tính toán tổng của tất cả các phần tử của mảng a. Đầu ra của chương trình là:

0 the element of a is 67.70  
1 the element of a is 89.80  
2 the element of a is 21.00  
3 the element of a is 78.00

sum of all elements of a 256.5  

Trong trường hợp bạn chỉ muốn giá trị và muốn bỏ qua chỉ mục, bạn có thể thực hiện việc này bằng cách thay thế chỉ mục bằng ký tự nhận dạng trống _.

for _, v := range a { //ignores index  
}

Vòng lặp for ở trên bỏ qua chỉ mục. Tương tự, giá trị cũng có thể bị bỏ qua.

Mảng đa chiều

Các mảng chúng ta đã tạo cho đến nay đều là mảng một chiều. Có thể tạo mảng đa chiều.

package main

import (  
    "fmt"
)

func printarray(a [3][2]string) {  
    for _, v1 := range a {
        for _, v2 := range v1 {
            fmt.Printf("%s ", v2)
        }
        fmt.Printf("\n")
    }
}

func main() {  
    a := [3][2]string{
        {"lion", "tiger"},
        {"cat", "dog"},
        {"pigeon", "peacock"}, //this comma is necessary. The compiler will complain if you omit this comma
    }
    printarray(a)
    var b [3][2]string
    b[0][0] = "apple"
    b[0][1] = "samsung"
    b[1][0] = "microsoft"
    b[1][1] = "google"
    b[2][0] = "AT&T"
    b[2][1] = "T-Mobile"
    fmt.Printf("\n")
    printarray(b)
}

Chạy chương trình trong playground

Trong chương trình trên, tại dòng số 17, một mảng chuỗi hai chiều a đã được khai báo bằng cú pháp viết tắt. Dấu phẩy ở cuối dòng số 20 là cần thiết. Điều này là do lexer tự động chèn dấu chấm phẩy theo các quy tắc đơn giản. Vui lòng đọc bài viết dưới đây nếu bạn quan tâm để biết thêm về lý do tại sao cần có dấu chấm phẩy.

Effective Go - go.dev
Go is an open source programming language that makes it easy to build simple, reliable, and efficient software.

Một mảng 2 chiều khác là b được khai báo ở dòng số 23 và các chuỗi lần lượt được thêm vào nó cho mỗi chỉ mục. Đây là một cách khác để khởi tạo mảng 2 chiều.

Function printarray ở dòng 7 sử dụng hai vòng lặp for range để in nội dung của mảng 2 chiều. Chương trình trên sẽ in ra kết quả sau:

lion tiger  
cat dog  
pigeon peacock 

apple samsung  
microsoft google  
AT&T T-Mobile  

Mặc dù các mảng có vẻ đủ linh hoạt, nhưng chúng có hạn chế là chúng có độ dài cố định. Không thể tăng độ dài của một mảng. Đây là nơi slice được sử dụng. Thực tế trong Go, slice phổ biến hơn mảng thông thường.

Trong hướng dẫn tiếp theo, chúng ta sẽ tìm hiểu về slice trong Go.

Slice trong Go
Trong hướng dẫn này, chúng ta sẽ tìm hiểu cách tạo slice, sửa đổi slice, thêm phần tử vào slice, slice đa chiều, tối ưu bộ nhớ khi sử dụng slice trong Go.

Như thường lệ, cảm ơn bạn đã đọc. Hãy chia sẻ những nhận xét và phản hồi có giá trị của bạn.

Nếu Comdy hữu ích và giúp bạn tiết kiệm thời gian làm việc

Bạn có thể vui lòng đưa Comdy vào whitelist của trình chặn quảng cáo ❤️ để hỗ trợ chúng tôi trong việc trả tiền cho dịch vụ lưu trữ web để duy trì hoạt động của trang web.

Go
Bài Viết Liên Quan:
Phương thức (method) trong Go
Trung Nguyen 02/12/2021
Phương thức (method) trong Go

Trong hướng dẫn này, chúng ta sẽ tìm hiểu phương thức (method) trong Go là gì? Cú pháp khai báo phương thức, so sánh phương thức với hàm, ... trong Go.

Struct trong Go
Trung Nguyen 28/11/2021
Struct trong Go

Trong hướng dẫn này, chúng ta sẽ tìm hiểu struct là gì, cách khai báo và sử dụng một struct trong Go, struct ẩn danh, so sanh hai struct, ...

Con trỏ trong Go
Trung Nguyen 28/11/2021
Con trỏ trong Go

Trong hướng dẫn này, chúng ta sẽ tìm hiểu cách con trỏ (pointer) hoạt động trong Go và nó khác với con trỏ trong các ngôn ngữ khác như C và C++ như thế nào.

Chuỗi trong Go
Trung Nguyen 28/11/2021
Chuỗi trong Go

Chuỗi (string) xứng đáng được đề cập đặc biệt trong Go vì chúng khác biệt trong cách triển khai khi so sánh với các ngôn ngữ khác.