[Golang cơ bản] Slice trong Golang

Post on: 2023-03-14 01:25:01 | in: Golang
Trong Go, "slice" là một loại cấu trúc dữ liệu được sử dụng để đại diện cho một chuỗi các phần tử có cùng kiểu dữ liệu.

Tổng quan

Kích thước của mảng được tính toán trong đó giới hạn tính linh hoạt và khả năng của mảng trong Go. Đó là lý do tại sao Slice xuất hiện. Slice mạnh mẽ và thuận tiện hơn để sử dụng so với mảng. Thực tế, Slice tương đương hơn với mảng trong các ngôn ngữ lập trình khác.

Một Slice trỏ đến một mảng cơ sở và được đại diện bên trong bởi một tiêu đề Slice. Khác với mảng, kích thước của một Slice linh hoạt và có thể được thay đổi.

Đại diện bên trong của một Slice

Bên trong, một Slice được đại diện bởi ba thứ:

  • Con trỏ đến mảng cơ sở.
  • Độ dài hiện tại của mảng cơ sở.
  • Tổng dung lượng tối đa mà mảng cơ sở có thể mở rộng.

Đại diện bên trong này được miêu tả bởi cấu trúc SliceHeader, nó có dạng như sau:

type SliceHeader struct {
        Pointer uintptr
        Len  int
        Cap  int
}

Trường Pointer trong tiêu đề Slice là một con trỏ đến mảng cơ sở. Trường Len là độ dài hiện tại của Slice và trường Cap là dung lượng của Slice. Tương tự như mảng, chỉ số của một Slice bắt đầu từ số không đến độ dài của Slice trừ một. Vì vậy, một Slice có độ dài là 3 và dung lượng là 5 sẽ trông như sau:

Tạo một Slice

Có bốn cách để tạo một Slice

  • Sử dụng định dạng []{}
  • Tạo Slice từ Slice hoặc mảng khác
  • Sử dụng hàm make
  • Sử dụng hàm new

Hãy xem từng phương pháp một.

Sử dụng định dạng []{}

Cách thông dụng nhất để khai báo một Slice là:

s := []int

Nó khai báo một slice rỗng với độ dài và dung lượng đều bằng 0. Chúng ta cũng có thể khởi tạo slice trong quá trình khai báo.

s := []int{1,2}

Nó khai báo một slice của các số nguyên với độ dài là 2 và dung lượng cũng là 2. Dung lượng sẽ bằng số phần tử Slice được chỉ định thực tế. Chúng ta cũng có hai hàm thư viện được cung cấp bởi Go để biết độ dài và dung lượng của một Slice.

  • Hàm len() - trả về độ dài của Slice
  • Hàm cap() - trả về dung lượng của Slice

Hãy xem một chương trình nhỏ để hiển thị những điểm trên

package main

import "fmt"

func main() {
    sample := []int{}
    fmt.Println(len(sample))
    fmt.Println(cap(sample))
    fmt.Println(sample)

    letters := []string{"a", "b", "c"}
    fmt.Println(len(letters))
    fmt.Println(cap(letters))
    fmt.Println(letters)
}

Output

0
0
[]


3
3
[a b c]

Khi các phần tử thực tế không được chỉ định, thì cả độ dài và dung lượng của Slice đều bằng không. Khi các phần tử thực tế được chỉ định, cả độ dài và dung lượng đều bằng số lượng phần tử được chỉ định.

Tạo một Slice từ một Slice hoặc mảng khác

Một slice có thể được tạo bằng cách lấy một phần từ một slice hoặc mảng đã có.

Tạo một slice từ một mảng

Định dạng để tạo một slice mới bằng cách lấy một phần từ một mảng đã có là

[n]sample[start:end]

Thao tác trên sẽ trả về một slice mới từ mảng, bắt đầu từ chỉ mục bắt đầu đến chỉ mục kết thúc-1. Vì vậy, phần tử tại chỉ mục kết thúc không được bao gồm trong slice được tạo mới. Trong quá trình lấy một phần từ slice đã có, cả chỉ mục bắt đầu và kết thúc đều là tùy chọn.

  • Giá trị mặc định của chỉ mục bắt đầu là 0
  • Giá trị mặc định của chỉ mục kết thúc là độ dài của mảng

Hãy xem một ví dụ.

package main

import "fmt"

func main() {
    numbers := [5]int{1, 2, 3, 4, 5}

    //Both start and end
    num1 := numbers[2:4]
    fmt.Println("Both start and end")
    fmt.Printf("num1=%v\n", num1)
    fmt.Printf("length=%d\n", len(num1))
    fmt.Printf("capacity=%d\n", cap(num1))

    //Only start
    num2 := numbers[2:]
    fmt.Println("\nOnly start")
    fmt.Printf("num1=%v\n", num2)
    fmt.Printf("length=%d\n", len(num2))
    fmt.Printf("capacity=%d\n", cap(num2))

    //Only end
    num3 := numbers[:3]
    fmt.Println("\nOnly end")
    fmt.Printf("num1=%v\n", num3)
    fmt.Printf("length=%d\n", len(num3))
    fmt.Printf("capacity=%d\n", cap(num3))

    //None
    num4 := numbers[:]
    fmt.Println("\nOnly end")
    fmt.Printf("num1=%v\n", num4)
    fmt.Printf("length=%d\n", len(num4))
    fmt.Printf("capacity=%d\n", cap(num4))
}

Output

Both start and end
num1=[3 4]
length=2
capacity=3


Only start
num1=[3 4 5]
length=3
capacity=3

Only end
num1=[1 2 3]
length=3
capacity=5

Only end
num1=[1 2 3 4 5]
length=5
capacity=5

Chú ý rằng trong ví dụ trên

  • độ dài của slice mới được tạo ra = (endstart)
  • dung lượng của slice mới được tạo ra = (length_of_arraystart)

Slice num1 sẽ trông như sau:

Các slice mới được tạo vẫn tham chiếu đến mảng ban đầu. Để kiểm tra điều này, hãy thay đổi phần tử tại bất kỳ một chỉ số nào của mảng và sau đó in lại slice.

numbers[3] = 8
fmt.Printf("num1=%v\n", num2)
fmt.Printf("num3=%v\n", num3)
fmt.Printf("num4=%v\n", num4)

Đây là kết quả:

num1=[3 8 5]
num3=[1 2 3 8]
num4=[1 2 3 8 5]

Điều này chứng tỏ rằng mỗi slice mới vẫn đang tham chiếu đến mảng ban đầu.

Tạo slice từ slice

Những gì chúng ta đã thảo luận về việc cắt lại từ một mảng cũng áp dụng ở đây. Xem ví dụ bên dưới để minh họa điều tương tự.

package main

import "fmt"

func main() {
    numbers := []int{1, 2, 3, 4, 5}

    //Both start and end
    num1 := numbers[2:4]
    fmt.Println("Both start and end")
    fmt.Printf("num1=%v\n", num1)
    fmt.Printf("length=%d\n", len(num1))
    fmt.Printf("capacity=%d\n", cap(num1))

    //Only start
    num2 := numbers[2:]
    fmt.Println("\nOnly start")
    fmt.Printf("num1=%v\n", num2)
    fmt.Printf("length=%d\n", len(num2))
    fmt.Printf("capacity=%d\n", cap(num2))

    //Only end
    num3 := numbers[:3]
    fmt.Println("\nOnly end")
    fmt.Printf("num1=%v\n", num3)
    fmt.Printf("length=%d\n", len(num3))
    fmt.Printf("capacity=%d\n", cap(num3))

    //None
    num4 := numbers[:]
    fmt.Println("\nOnly end")
    fmt.Printf("num1=%v\n", num4)
    fmt.Printf("length=%d\n", len(num4))
    fmt.Printf("capacity=%d\n", cap(num4))
}

Output

Both start and end
num1=[3 4]
length=2
capacity=3


Only start
num1=[3 4 5]
length=3
capacity=3

Only end
num1=[1 2 3]
length=3
capacity=5

Only end
num1=[1 2 3 4 5]
length=5
capacity=5

Điều này chứng tỏ rằng mỗi slice mới được tạo vẫn đang tham chiếu đến mảng gốc ban đầu.  Để kiểm tra điều này, hãy thay đổi phần tử tại bất kỳ một chỉ mục nào đó của slice ban đầu và sau đó in lại tất cả các slice mới được tạo ra.

numbers[3] = 8
fmt.Printf("num1=%v\n", num2)
fmt.Printf("num3=%v\n", num3)
fmt.Printf("num4=%v\n", num4)

Đây là kết quả:

num1=[3 8 5]
num3=[1 2 3 8]
num4=[1 2 3 8 5]

Sử dụng hàm make

make là một hàm builtin do go cung cấp cũng có thể được sử dụng để tạo một slice. Dưới đây là chữ ký của hàm make:

func make([]{type}, length, capacity int) []{type}

Capacity là một tham số tùy chọn khi tạo slice bằng cách sử dụng hàm make. Khi bỏ qua capacity, capacity của slice sẽ bằng độ dài được chỉ định cho slice. Khi sử dụng hàm make, Go sẽ cấp phát một mảng có độ dài bằng với capacity. Tất cả các phần tử của mảng được cấp phát sẽ được khởi tạo với giá trị mặc định bằng 0 của kiểu dữ liệu tương ứng. Hãy xem một chương trình minh họa điều này.

package main

import "fmt"

func main() {
    numbers := make([]int, 3, 5)
    fmt.Printf("numbers=%v\n", numbers)
    fmt.Printf("length=%d\n", len(numbers))
    fmt.Printf("capacity=%d\n", cap(numbers))

    //With capacity ommited
    numbers = make([]int, 3)
    fmt.Println("\nCapacity Ommited")
    fmt.Printf("numbers=%v\n", numbers)
    fmt.Printf("length=%d\n", len(numbers))
    fmt.Printf("capacity=%d\n", cap(numbers))
}

Output

numbers=[0 0 0]
length=3
capacity=5


Capacity Ommited
numbers=[0 0 0]
length=3
capacity=3

Sử dụng hàm new

new là một hàm tích hợp sẵn được cung cấp bởi Go để tạo một slice. Nó không phải là cách phổ biến để tạo một slice vì make có nhiều tính năng linh hoạt hơn. Hàm new thường không được sử dụng và cũng trả về một con trỏ đến slice nil. Hãy xem một ví dụ. Trong ví dụ dưới đây, chúng ta sử dụng toán tử giải tham chiếu '*' vì hàm new trả về một con trỏ đến slice nil.

package main

import "fmt"

func main() {
    numbers := new([]int)
    fmt.Printf("numbers=%v\n", *numbers)
    fmt.Printf("length=%d\n", len(*numbers))
    fmt.Printf("capacity=%d\n", cap(*numbers))
}

Output

numbers=[]
length=0
capacity=0

Độ dài (length) và sức chứa (capacity) của slice

Trước khi tiếp tục, hãy tập trung vào việc hiểu rõ các lưu ý về độ dài và sức chứa. Hãy tạo một slice đơn giản với sức chứa lớn hơn độ dài.

numbers := make([]int, 3, 5)
  • Truy cập slice vượt quá giới hạn độ dài của nó sẽ dẫn đến lỗi "Index out of range" trong thời gian chạy. Không quan trọng nếu chỉ số được truy cập nằm trong dung lượng của slice. Vì vậy, dòng code dưới đây sẽ gây ra lỗi thời gian chạy.
numbers[4] = 5
  • Độ dài của slice có thể tăng lên đến khả năng chứa của nó bằng cách cắt lại. Vì vậy, phép cắt lại dưới đây sẽ tăng độ dài từ 3 lên 5.
numbers = numbers[0:5]
  • Độ dài của slice cũng có thể được giảm bằng cách sử dụng re-slicing. Vì vậy, dưới đây là ví dụ về re-slicing để giảm độ dài từ 3 xuống 2.
numbers = numbers[0:2]
  • Sự lợi ích của việc có capacity là có thể cấp phát trước một mảng có kích thước bằng với capacity trong quá trình khởi tạo. Điều này giúp tăng hiệu suất vì nếu cần thêm các phần tử khác vào mảng này thì không cần phải cấp phát thêm bộ nhớ.

Dưới đây là một chương trình minh họa cho những điểm nêu trên.

package main

import "fmt"

func main() {
    numbers := make([]int, 3, 5)
    fmt.Printf("numbers=%v\n", numbers)
    fmt.Printf("length=%d\n", len(numbers))
    fmt.Printf("capacity=%d\n", cap(numbers))

    //This line will cause a runtime error index out of range [4] with length 3
    //numbers[4] = 5

    //Increasing the length from 3 to 5
    numbers = numbers[0:5]
    fmt.Println("\nIncreasing length from 3 to 5")
    fmt.Printf("numbers=%v\n", numbers)
    fmt.Printf("length=%d\n", len(numbers))
    fmt.Printf("capacity=%d\n", cap(numbers))

    //Decresing the length from 3 to 2
    numbers = numbers[0:2]
    fmt.Println("\nDecreasing length from 3 to 2")
    fmt.Printf("numbers=%v\n", numbers)
    fmt.Printf("length=%d\n", len(numbers))
    fmt.Printf("capacity=%d\n", cap(numbers))
}

Output

numbers=[0 0 0]
length=3
capacity=5


Increasing length from 3 to 5
numbers=[0 0 0 0 0]
length=5
capacity=5

Decreasing length from 3 to 2
numbers=[0 0]
length=2
capacity=5

Truy cập và sửa đổi các phần tử của Slice

Một phần tử slice có thể được truy cập bằng cách chỉ định chỉ số. Phần tử slice cũng có thể được gán giá trị mới bằng cách sử dụng chỉ số. Ngoài ra, lưu ý rằng bất kỳ thay đổi nào trong mảng cơ sở sẽ phản ánh lại trên slice như chúng ta đã thấy ở trên. Hãy xem một ví dụ nhỏ về việc truy cập và sửa đổi slice.

package main

import "fmt"

func main() {
    array := [5]int{1, 2, 3, 4, 5}
    slice := array[:]

    //Modifying the slice
    slice[1] = 7
    fmt.Println("Modifying Slice")
    fmt.Printf("Array=%v\n", array)
    fmt.Printf("Slice=%v\n", slice)

    //Modifying the array. Would reflect back in slice too
    array[1] = 2
    fmt.Println("\nModifying Underlying Array")
    fmt.Printf("Array=%v\n", array)
    fmt.Printf("Slice=%v\n", slice)
}

Output

Modifying Slice
Array=[1 7 3 4 5]
Slice=[1 7 3 4 5]


Modifying Underlying Array
Array=[1 2 3 4 5]
Slice=[1 2 3 4 5]

Các cách khác nhau để duyệt một slice

Có thể duyệt slice bằng các cách sau:

  • Sử dụng vòng lặp for
  • Sử dụng vòng lặp for-range

Hãy xem một ví dụ về cách thực hiện cả hai cách trên.

package main

import "fmt"

func main() {
    letters := []string{"a", "b", "c"}
    //Using for loop
    fmt.Println("Using for loop")
    len := len(letters)
    for i := 0; i < len; i++ {
        fmt.Println(letters[i])
    }

    //Using for-range operator
    fmt.Println("\nUsing for-range loop")
    for i, letter := range letters {
        fmt.Printf("%d %s\n", i, letter)
    }
}

Output

Using for loop
a
b
c


Using for-range loop
0 a
1 b
2 c

Thêm phần tử vào slice

Gói builtin của go cung cấp hàm append có thể được sử dụng để thêm vào cuối slice. Dưới đây là chữ ký của hàm này.

func append(slice []Type, elems ...Type) []Type

Đối số đầu tiên là slice chính nó.  Đối số thứ hai là số biến đổi có thể thay đổi, nó là các giá trị cần được thêm vào slice

elems ...Type

Toán tử '...' là cú pháp biến thiên. Vì vậy, ...Type nghĩa là hàm append có thể chấp nhận một số lượng biến số các đối số của kiểu Type. Dưới đây là cách sử dụng hàm này. Trong đoạn code dưới đây, chúng ta đang thêm số 4 vào một slice có hai phần tử. Nó được thêm vào cuối và trả về slice ban đầu. Đó là lý do tại sao chúng ta lại lưu kết quả vào biến numbers. Cũng hoàn toàn hợp lệ nếu gán kết quả cho một biến khác.

numbers := []int{1,2}
numbers = append(numbers, 4) //Slice will become [1, 2, 4]

Việc thêm một số lượng phần tử lớn cũng hoàn toàn được vì đối số thứ hai là đối số biến thiên.

numbers := []int{1,2}
numbers = append(numbers, 3, 4, 5) //Slice will become [1, 2, 3, 4, 5]

Hàm này trong nền tảng sẽ tăng độ dài và khả năng của slice. Có hai trường hợp

  • Khi độ dài slice nhỏ hơn khả năng.

Trong trường hợp này, khi sử dụng hàm append, độ dài của slice sẽ được tăng lên một mà không có bất kỳ thay đổi nào về khả năng của nó. Hãy xem một ví dụ.

package main

import "fmt"

func main() {
    numbers := make([]int, 3, 5)
    numbers[0] = 1
    numbers[1] = 2
    numbers[2] = 3
    fmt.Printf("numbers=%v\n", numbers)
    fmt.Printf("length=%d\n", len(numbers))
    fmt.Printf("capacity=%d\n", cap(numbers))

    //Append number 4
    numbers = append(numbers, 4)
    fmt.Println("\nAppend Number 4")
    fmt.Printf("numbers=%v\n", numbers)
    fmt.Printf("length=%d\n", len(numbers))
    fmt.Printf("capacity=%d\n", cap(numbers))

    //Append number 5
    numbers = append(numbers, 4)
    fmt.Println("\nAppend Number 5")
    fmt.Printf("numbers=%v\n", numbers)
    fmt.Printf("length=%d\n", len(numbers))
    fmt.Printf("capacity=%d\n", cap(numbers))
}

Output

numbers=[1 2 3]
length=3
capacity=5


Append Number 4
numbers=[1 2 3 4]
length=4
capacity=5

Append Number 5
numbers=[1 2 3 4 4]
length=5
capacity=5

Đối với cả hai trường hợp, dung lượng (capacity) của slice không thay đổi và nó vẫn là 5, trong khi độ dài (length) tăng lên 1.

  • Khi độ dài slice lớn hơn khả năng chứa.

Trong trường hợp này, vì không còn đủ dung lượng, nên không thể chứa thêm phần tử mới. Vì vậy, ở dưới nền tảng một mảng gấp đôi dung lượng sẽ được phân bổ. Mảng hiện tại được trỏ bởi slice sẽ được sao chép vào mảng mới đó. Bây giờ slice sẽ bắt đầu trỏ đến mảng mới này. Do đó, dung lượng sẽ tăng gấp đôi và độ dài sẽ tăng thêm 1. Hãy xem một ví dụ.

package main

import "fmt"

func main() {
    numbers := make([]int, 3, 3)
    numbers[0] = 1
    numbers[1] = 2
    numbers[2] = 3

    fmt.Printf("numbers=%v\n", numbers)
    fmt.Printf("length=%d\n", len(numbers))
    fmt.Printf("capacity=%d\n", cap(numbers))

    //Append number 4
    numbers = append(numbers, 4)
    fmt.Println("\nAppend Number 4")
    fmt.Printf("numbers=%v\n", numbers)
    fmt.Printf("length=%d\n", len(numbers))
    fmt.Printf("capacity=%d\n", cap(numbers))
}

Output

numbers=[1 2 3]
length=3
capacity=3


Append Number 4
numbers=[1 2 3 4]
length=4
capacity=6

Chú ý rằng trong ví dụ trên, dung lượng đã tăng gấp đôi.

Cũng có thể nối một slice vào một slice khác. Dưới đây là định dạng cho việc đó.

res := append(slice1, slice2...)

Chú ý đến dấu '...' sau slice thứ hai. '...' là toán tử cho biết đối số đó là một tham số biến thiên. Có nghĩa là trong thời gian chạy, slice2 sẽ được mở rộng thành các phần tử riêng lẻ của nó và được truyền dưới dạng nhiều đối số cho hàm append.

package main

import "fmt"

func main() {
    numbers1 := []int{1, 2}
    numbers2 := []int{3, 4}
    numbers := append(numbers1, numbers2...)
    fmt.Printf("numbers=%v\n", numbers)
    fmt.Printf("length=%d\n", len(numbers))
    fmt.Printf("capacity=%d\n", cap(numbers))
}

Output

numbers=[1 2 3 4]
length=4
capacity=4

Sao chép một slice

Golang cung cấp hàm copy trong gói builtin, được sử dụng để sao chép một slice. Dưới đây là cú pháp của hàm này. Nó nhận vào hai slice là dstsrc, và sao chép dữ liệu từ src sang dst. Nó trả về số lượng phần tử đã được sao chép.

func copy(dst, src []Type) int

Có hai trường hợp cần xem xét khi sử dụng hàm copy:

  • Nếu chiều dài của src lớn hơn chiều dài của dst, thì số phần tử được sao chép là chiều dài của dst.
  • Nếu chiều dài của dst lớn hơn chiều dài của src, thì số phần tử được sao chép là chiều dài của src.

Về cơ bản số phần tử được sao chép là số nhỏ nhất giữa chiều dài của (src, dst).

Cũng lưu ý rằng sau khi sao chép thì bất kỳ thay đổi nào trong dst sẽ không phản ánh trong src và ngược lại. Chúng ta hãy xem một ví dụ về điều này.

package main

import "fmt"

func main() {
    src := []int{1, 2, 3, 4, 5}
    dst := make([]int, 5)

    numberOfElementsCopied := copy(dst, src)
    fmt.Printf("Number Of Elements Copied: %d\n", numberOfElementsCopied)
    fmt.Printf("dst: %v\n", dst)
    fmt.Printf("src: %v\n", src)

    //After changing numbers2
    dst[0] = 10
    fmt.Println("\nAfter changing dst")
    fmt.Printf("dst: %v\n", dst)
    fmt.Printf("src: %v\n", src)
}

Output

Number Of Elements Copied: 5
dst: [1 2 3 4 5]
src: [1 2 3 4 5]


After changing dst
dst: [10 2 3 4 5]
src: [1 2 3 4 5]

Nil Slice

Giá trị mặc định zero của một slice là nil. Độ dài và sức chứa của một slice nil đều bằng 0. Tuy nhiên, việc thêm phần tử vào slice nil vẫn làm được. Hãy xem một ví dụ.

package main

import "fmt"

func main() {
    var numbers []int
    fmt.Printf("numbers=%v\n", numbers)
    fmt.Printf("length=%d\n", len(numbers))
    fmt.Printf("capacity=%d\n", cap(numbers))
    numbers = append(numbers, 1)
    fmt.Printf("numbers=%v\n", numbers)
    fmt.Printf("length=%d\n", len(numbers))
    fmt.Printf("capacity=%d\n", cap(numbers))
}

Output

numbers=[]
length=0
capacity=0
numbers=[1]
length=1
capacity=1

Slices đa chiều

Giống như mảng đa chiều là một mảng các mảng, tương tự, đa chiều slice là một slice của các slice. Để hiểu điều này, hãy xem định nghĩa của một slice.

Trường data trong tiêu đề slice là con trỏ tới mảng bên dưới. Đối với một slice một chiều, chúng ta có khai báo như sau.

oneDSlice := make([]int, 2)

To declare a two dimensional slice the declaration would be

twoDSlice = make([][]int, 2)

Trong khai báo trên, chúng ta muốn tạo một slice có 2 slice bên trong. Hãy hiểu kỹ điểm này. Nhưng đợi một chút, ở đây chúng ta chưa chỉ định kích thước của chiều thứ hai, có nghĩa là độ dài của mỗi slice con bên trong là bao nhiêu. Trong trường hợp của slice, mỗi slice con bên trong phải được khởi tạo rõ ràng như sau:

for i := range twoDSlice {
    twoDSlice[i] = make([]int, 3)
}

Vì các slice trong một slice là các slice riêng biệt, nên chúng ta có thể tạo ra các slice có chiều dài khác nhau bên trong một slice đa chiều. Ví dụ dưới đây tạo ra một slice đa chiều với 2 slice bên trong, mỗi slice có chiều dài khác nhau:

var twoDSlice = make([][]int, 2)
twoDSlice[0] = []int{1, 2, 3}
twoDSlice[1] = []int{4, 5, 6}

Điều cơ bản là, với khai báo trên, chúng ta tạo ra một slice 2 * 3 chiều là một slice hai chiều. Ý tưởng tương tự có thể được mở rộng cho hai chiều, ba chiều và cũng như thế.

Một ví dụ hoạt động hoàn chỉnh của hai điểm trên

package main

import "fmt"

func main() {
    twoDSlice1 := make([][]int, 3)
    for i := range twoDSlice1 {
        twoDSlice1[i] = make([]int, 3)
    }
    fmt.Printf("Number of rows in slice: %d\n", len(twoDSlice1))
    fmt.Printf("Number of columns in arsliceray: %d\n", len(twoDSlice1[0]))
    fmt.Printf("Total number of elements in slice: %d\n", len(twoDSlice1)*len(twoDSlice1[0]))
    fmt.Println("First Slice")
    for _, row := range twoDSlice1 {
        for _, val := range row {
            fmt.Println(val)
        }
    }
    twoDSlice2 := make([][]int, 2)
    twoDSlice2[0] = []int{1, 2, 3}
    twoDSlice2[1] = []int{4, 5, 6}
    fmt.Println()
    fmt.Printf("Number of rows in slice: %d\n", len(twoDSlice2))
    fmt.Printf("Number of columns in arsliceray: %d\n", len(twoDSlice2[0]))
    fmt.Printf("Total number of elements in slice: %d\n", len(twoDSlice2)*len(twoDSlice2[0]))
    fmt.Println("Second Slice")
    for _, row := range twoDSlice2 {
        for _, val := range row {
            fmt.Println(val)
        }
    }
}

Output

Number of rows in slice: 2
Number of columns in arsliceray: 3
Total number of elements in slice: 6
First Slice
0
0
0
0
0
0


Number of rows in slice: 2
Number of columns in arsliceray: 3
Total number of elements in slice: 6
Second Slice
1
2
3
4
5
6

Chúng ta đã đề cập ở trên rằng chúng ta đang tạo một slice hai chiều có kích thước 2 * 3. Với điều đó, suy nghĩ có thể đang xuất hiện trong đầu bạn là liệu có thể có chiều dài khác nhau cho các slice bên trong. Có thể. Khác với mảng có các mảng con cùng chiều dài, trong trường hợp slice, vì chúng ta khởi tạo từng slice con một cách riêng lẻ, vì vậy có thể có chiều dài khác nhau cho các slice con

Hãy xem một ví dụ.

package main

import "fmt"

func main() {
    twoDSlice := make([][]int, 2)
    twoDSlice[0] = []int{1, 2, 3}
    twoDSlice[1] = []int{4, 5}

    fmt.Printf("Number of rows in slice: %d\n", len(twoDSlice))
    fmt.Printf("Len of first row: %d\n", len(twoDSlice[0]))
    fmt.Printf("Len of second row: %d\n", len(twoDSlice[1]))
    fmt.Println("Traversing slice")
    for _, row := range twoDSlice {
        for _, val := range row {
            fmt.Println(val)
        }
    }
}

Output

Number of rows in slice: 2
Len of first row: 3
Len of second row: 2
Traversing slice
1
2
3
4
5

Dưới đây là một ví dụ nhỏ về slice ba chiều. Trong chương trình dưới đây, chúng ta tạo một slice có kích thước 2x2x3.

package main

import "fmt"

func main() {
    sample := make([][][]int, 2)
    for i := range sample {
        sample[i] = make([][]int, 2)
        for j := range sample[i] {
            sample[i][j] = make([]int, 3)
        }
    }

    fmt.Printf("Length of first dimension: %d\n", len(sample))
    fmt.Printf("Length of second dimension: %d\n", len(sample[0]))
    fmt.Printf("Length of third dimension: %d\n", len(sample[0][0]))
    fmt.Printf("Overall Dimension of the slice: %d*%d*%d\n", len(sample), len(sample[0]), len(sample[0][0]))
    fmt.Printf("Total number of elements in slice: %d\n", len(sample)*len(sample[0])*len(sample[0][0]))
    for _, first := range sample {
        for _, second := range first {
            for _, value := range second {
                fmt.Println(value)
            }
        }
    }
}

Output

Length of first dimension: 2
Length of second dimension: 2
Length of third dimension: 3
Overall Dimension of the slice: 2*2*3
Total number of elements in slice: 12
0
0
0
0
0
0
0
0
0
0
0
0

Kết luận

Đây là tất cả những điều cơ bản về slice trong Golang. Hy vọng bạn đã thích bài viết này. Hãy chia sẻ phản hồi / cải tiến / sai sót trong phần bình luận.

 
Tag: golang cơ bản go