Khai báo mảng
Kiểu mảng (array) được viết là [N]T: một độ dài, rồi đến kiểu phần tử. Độ dài được cố định lúc biên dịch và là một phần của kiểu.
Đầu ra:
[90 0 0 0 75] 5
Tue
[5]int [2 3 5 7 11]
["" "b" "" "d" ""]
Mảng luôn được khởi tạo đầy đủ. Phần tử nào bạn không gán sẽ mang giá trị zero của kiểu phần tử: 0, "", false, nil.
Chỉ số và giới hạn
Chỉ số chạy từ 0 đến len(a)-1. Một chỉ số hằng nằm ngoài phạm vi là lỗi biên dịch. Một chỉ số là biến nằm ngoài phạm vi sẽ gây panic lúc chạy.
var a [3]int
a[5] = 1 // compile error: invalid argument: index 5 out of bounds [0:3]
i := 5
a[i] = 1 // panic: runtime error: index out of range [5] with length 3
Duyệt mảng
for i, v := range a đi qua từng phần tử theo thứ tự. v là bản sao của phần tử, nên gán vào v không làm thay đổi mảng. Hãy ghi qua chỉ số.
Có thêm một điểm tinh tế chỉ riêng mảng mới có: range trên một giá trị mảng sẽ lấy bản sao của mảng ngay từ đầu. Nếu thân vòng lặp sửa các phần tử phía sau, v vẫn cho thấy giá trị cũ. Duyệt qua &a hoặc a[:] sẽ tránh được bản sao này.
Mảng là giá trị
Đây là điểm khác biệt chính so với mảng trong C, Java hay JavaScript. Gán một mảng sẽ sao chép mọi phần tử. Truyền mảng vào hàm cũng sao chép nó.
Đầu ra:
[1 2 3] [99 2 3]
[1 2 3]
[0 2 3]
Sao chép mảng nhỏ thì rẻ, mảng lớn thì tốn: truyền một [1_000_000]int theo giá trị sẽ sao chép tám megabyte. Hãy truyền con trỏ (*[N]T) hoặc slice (a[:]) khi mảng lớn hoặc khi hàm cần thay đổi nó. Truy cập chỉ số qua con trỏ mảng không cần viết *: a[0] dùng được trên một *[3]int.
So sánh mảng và dùng mảng làm khóa của map
Mảng gồm các phần tử so sánh được hỗ trợ == và !=. Hai mảng bằng nhau khi từng phần tử bằng nhau. Điều đó cũng khiến mảng hợp lệ để làm khóa của map, còn slice thì không.
Các mảng có độ dài khác nhau là các kiểu khác nhau và hoàn toàn không so sánh được: [3]int{} == [4]int{} không biên dịch được (mismatched types [3]int and [4]int).
Mảng nhiều chiều
Một mảng chứa các mảng cho bạn một lưới cố định. Toàn bộ lưới là một khối bộ nhớ liền mạch.
row[:] biến hàng [3]rune thành một slice []rune, kiểu mà string() chấp nhận.
Mảng so với slice
Mảng [N]T | Slice []T | |
|---|---|---|
| Độ dài | cố định, là một phần của kiểu | thay đổi qua append |
| Gán và truyền | sao chép mọi phần tử | sao chép một header nhỏ, dùng chung phần tử |
| Giá trị zero | N phần tử zero | nil, độ dài 0 |
== | có, từng phần tử một | chỉ khi so với nil |
| Khóa của map | có | không |
| Cách dùng thường gặp | dữ liệu kích thước cố định: hash, tọa độ, buffer | hầu hết mọi trường hợp khác |
Trên thực tế, bạn chủ yếu gặp mảng ở vài chỗ: sha256.Sum256 trả về một [32]byte, các bảng tra cứu có kích thước không bao giờ đổi, và làm vùng lưu trữ phía sau một slice. Cắt mảng bằng a[:] hoặc a[1:3] tạo ra một slice dùng chung bộ nhớ với mảng; trang slices giải thích việc dùng chung này nghĩa là gì.
Chuyển đổi giữa mảng và slice
Chuyển một slice thành mảng (Go 1.20) hoặc con trỏ mảng (Go 1.17) sẽ gây panic nếu slice ngắn hơn độ dài mảng: panic: runtime error: cannot convert slice with length 2 to array or pointer to array with length 3.
Lỗi thường gặp
- Mong một hàm sửa được mảng truyền vào. Hàm chỉ nhận một bản sao. Hãy truyền
*[N]Thoặc slice. - Dùng mảng ở chỗ cần slice.
[]int{1, 2}là slice,[2]int{1, 2}là mảng. Một hàm nhận[]intsẽ không chấp nhận[2]int; hãy truyềna[:]. - Mảng lớn trong vòng lặp range.
for _, v := range bigArraysao chép toàn bộ mảng trước. Hãy duyệt qua&bigArrayhoặc một slice.
Câu hỏi thường gặp
Khai báo mảng trong Go như thế nào?
Viết độ dài trong ngoặc vuông, trước kiểu phần tử: var a [5]int tạo năm số 0. Kèm giá trị: a := [3]string{"x", "y", "z"}. Dùng [...] để trình biên dịch tự đếm: a := [...]int{1, 2, 3} có kiểu [3]int.
Mảng và slice trong Go khác nhau thế nào?
Mảng có độ dài cố định thuộc về kiểu của nó ([3]int và [4]int là hai kiểu khác nhau) và được sao chép khi gán hoặc truyền vào hàm. Slice ([]int) là một khung nhìn vào một mảng nằm bên dưới, có độ dài thay đổi được qua append, và sao chép slice chỉ sao chép khung nhìn đó, không sao chép các phần tử. Phần lớn code Go dùng slice.
Lấy độ dài của mảng trong Go bằng cách nào?
Dùng hàm có sẵn len(a). Với mảng, đây là hằng số biết trước lúc biên dịch, nên bạn dùng được nó trong biểu thức hằng. cap(a) trả về cùng con số đó.
Có so sánh được hai mảng trong Go không?
Có, bằng == và !=, nếu kiểu phần tử so sánh được. Hai mảng bằng nhau khi mọi phần tử của chúng bằng nhau. Mảng cũng dùng được làm khóa của map. Slice thì không so sánh được bằng == (trừ khi so với nil); với slice, hãy dùng slices.Equal.