Mảng ở Golang

Tram Ho

1. Mảng và các nguyên tắc cơ bản

Hôm nay mình sẽ nói về mảng (array) trong Go . Mảng là một cấu trúc dữ liệu quan trọng trong hầu hết các ngôn ngữ lập trình. Trong Go cũng không phải ngoại lệ. Những cấu trúc như slicesmaps trong Go cũng được xây dựng dựa trên mảng. Hiểu về cách hoạt động của mảng sẽ giúp các bạn nhận ra được sức mạnh của slicesmaps trong Go.

1.1 Khái niệm

Một mảng (array) trong Go là kiểu dữ liệu có độ dài cố định chứa một khối dữ liệu cùng kiểu. Nó có thể là những kiểu nguyên thuỷ như int và string hoặc nó có thể có kiểu là struct.

Bạn có thể xem ví dụ bên dưới minh hoạ về mảng (array). Những phần tử của một mảng được đặt bên trong một chiếc hộp màu grey và được đặt liên tiếp nhau. Mỗi phần tử có cùng kiểu, trong ảnh minh hoạ nó là một integer, và nó có thể được truy cập thông qua một vị trí duy nhất

Mảng là một cấu trúc dữ liệu có giá trị. Bạn có thể truy cập từng phần tử của mảng một cách nhanh chóng khi sử dụng index. Kiểu của mảng cung cấp khoảng cách trong bộ nhớ mà bạn phải di chuyển để tìm từng phần tử. Vì mỗi phần tử là cùng kiểu và nối tiếp nhau nên việc di chuyển giữa các phần tử trong mảng là nhất quán và nhanh chóng.

1.2 Khai báo và khởi tạo (Declaring and initializing)

Một mảng (array) được khai báo bằng cách chỉ định loại dữ liệu được lưu trữ và tổng số phần từ (hay còn gọi là độ dài của mảng)

Khi một mảng được khai báo, thì kiểu dữ liệu và và độ dài của mảng đều không thể thay đổi được nữa. Nếu bạn cần thêm phần tử, bận cần tạo mới một mảng với độ dài mong muốn, sau đó sao chép giá trị từ mảng cũ sang mảng mới.

Khi một biến trong Go được khai báo, chúng luôn luôn được gán một giá trị khởi tạo mặc định, và mảng cũng không phải ngoại lệ. Khi một mảng được khởi tạo, mỗi phần tử được riêng biệt được khởi tạo về giá trị 0. Bạn có thể xem hình minh hoạ bên dưới, mỗi phần tử trong một mảng kiểu integer sẽ được khởi tạo giá trị là 0

Một cách nhanh và đơn giản để khai báo và khởi tạo một mảng là sử dụng array literal (từ này mình cũng chưa biết dịch sao cho thuận nghĩa). Array literals cho phép bạn khai báo số lượng phần tử và giá trị của từng phần tử trong mảng

Nếu độ dài của mảng nhận vào , Go sẽ xác định độ dài của mảng dựa vào số phần tử được khởi tạo.

1.3 Làm việc với mảng (Working with arrays)

Như bạn đã biết, mảng là một cấu trúc dữ liệu hiệu quả vì bộ nhớ được sắp xếp theo trình tự. Điều này mang lại lợi thế khi truy cập từng phần tử của mảng. Để truy cập từng phần tử của mảng sử dụng toán tử [ ]

Bạn cũng có thể sử dụng con trỏ với mảng. Sử dụng toán toán tử star(*) để truy cập vào giá trị mà mỗi con trỏ trỏ đến. Hình minh hoạ bên dưới:

Trong Go, mảng là một giá trị. Điều này nghĩa là bạn có thể dùng nó với toán tử gán (assignment operation). Tên biến biểu thị toàn bộ mảng, do đó bạn có thể gán cho các mảng cùng kiểu.

Sau khi thực hiện toán tử gán, bạn sẽ có 2 mảng giống hệt nhau. Như hình minh hoạ bên dưới:

Loại của một biến mảng bao gồm cả độ dài và kiểu dữ liệu nó có thể lưu trữ cho mỗi phần tử. Bạn chỉ có thể sử dụng toán tử gán với những mảng có cùng kiểu

Sao chép một mảng con trỏ sẽ sao chép giá trị của con trỏ chứ không phải giá trị mà con trỏ đang trỏ tới.

Sau khi sao chép, bạn có 2 mảng cùng trỏ đến một string. Như hình mình hoạ bên dưới:

1.4 Truyền mảng giữa các hàm (Passing arrays between functions)

Truyền mảng giữa các hàm có thể ảnh hưởng đến hiệu năng và tốn kém về bộ nhớ. Khi bạn truyền một biến giữa các hàm, chúng luôn luôn truyền giá trị. Khi biến của bạn là một mảng, bất kể kích thước của nó là gì, nó sẽ tạo ra một bản sao và truyền nó vào hàm. Xem ví dụ minh hoạ bên dưới:

Mỗi khi hàm foo được gọi 8 megabytes sẽ được phân bổ trên stack, sau đó chúng được sao chép và truyền vào hàm foo. Go có thể xử lý hành động sao chép này, nhưng để hiệu quả hơn, bạn nên truyền vào một con trỏ và nó chỉ copy 8 bytes, thay vì 8 megabytes của bộ nhớ được phân bổ trên stack.

Khi hàm foo được gọi sẽ nhận vào một con trỏ tới một mảng có 1 triệu phần tử kiểu integer. Hàm bây giờ sẽ nhận vào địa chỉ của mảng, điều này chỉ yêu cầu 8 bytes của bộ nhớ được phân bổ trên stack cho biến con trỏ.
Hành động này sẽ giúp tối ưu về hiểu năng trên bộ nhớ. Bạn chỉ cần nhận biết điều này vì khi bạn sử dụng con trỏ, thay đổi giá trị mà con trỏ đang trỏ tới sẽ thay đổi giá trị trên bộ nhớ đã được chia sẻ.

Bài viết về mảng khá dài, tuy nhiên mình nghĩ nội dung này cũng cấp khá đầy đủ về cách khai báo, cách sử dụng, cũng như là cách tối ưu khi sử dụng mảng truyền giữa các hàm. Nếu các bạn thấy hay thì hãy cho mình xin 1 like để mình có động lực làm thêm những bài sau nữa nhé

Link bài viết gốc đây các bạn nhé https://chiasekienthuc.netlify.app/blog/arrays.

Chia sẻ bài viết ngay

Nguồn bài viết : Viblo